Binary, CLI and Desktop App Naming

mandatory

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.

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 multibinary tooling family (e.g., `dev/kodertools`)
  • Packaging a product as a .deb, .rpm, .AppImage, or .kpkg
  • Setting the D-Bus application ID for a Linux desktop app
  • Creating a .desktop file or systemd service unit for a Koder product
  • Deciding the CLI command name a user types to launch a product

1. Core rule: binary = aliases[0] || canonical slug

*pdated 20260511*to consume meta/docs/stack/registries/component-names.md as the source of truth, per specs/naming/forms.kmd. The previous rule ("slug = binary = install") used a compact slug (kterm); the new model separates the *anonical slug*(koder-term, kebab-case, registered) from the *inary name*(kterm, taken from aliases[0] of the registry entry, if present). Existing binaries keep their current names — no migration required.

Every Koder product or CLI has a row in meta/docs/stack/registries/component-names.md with these forms:

  • slug — canonical kebabcase identifier (e.g. `koderterm`)
  • aliases — optional compact forms (e.g. [kterm])

The *inary name*and *pkg install argument*derive as:

binary_name  =  aliases[0]  ||  slug
install_arg  =  binary_name  (identical)

Worked examples:

Component Registry slug aliases[0] Binary name Install command
Koder Term koder-term kterm kterm kpkg install kterm
Koder Hub koder-hub khub khub kpkg install khub
Koder Koda koda koda kpkg install koda
Koder Tools koder-tools ktools ktools kpkg install ktools
Koder Kdedup kdedup kdedup kpkg install kdedup

The binary name is *ever transformed*at packaging time — what the registry says is what ships. New products choose their registry row at creation (see specs/naming/forms.kmd §2 Rules); everything else derives.

Slug naming convention (deprecated in favor of registry)

The previous convention "start the slug with k" is superseded. New products register a canonical kebab slug per specs/naming/forms.kmd R4 (with or without koder- prefix, per type and bare initial) and add a k-prefixed alias only when justified by policies/naming-aliases.kmd. The k-prefix appears in the binary via aliases[0], not in the slug.

1.0 Display name parity rule

The slug is also the source of truth for the *isplay 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, whitelabel, legalmandated 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

*pdated 20260511* <binary> below is the registry-derived aliases[0] || slug (per §1). <slug> (kebab canonical) is only used in path segments inside /opt/koder/, mirroring the registry slug.

/opt/koder/<binary>/        ← product bundle root (binary name)
    <binary>                ← main executable (name = aliases[0] || slug)
    lib/                    ← shared libraries (.so)
    data/                   ← Flutter assets, fonts, ICU
/usr/local/bin/<binary>     ← symlink → /opt/koder/<binary>/<binary>

The symlink in /usr/local/bin/ is the user-facing CLI command.

*egacy cleanup:*if a previous version installed under /opt/koder-<binary>/, the .deb prerm script must remove that directory on upgrade:

# DEBIAN/prerm
#!/bin/sh
rm -rf /opt/koder-<binary> 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 *Lib application ID*and the *esktop file name*(see §5). It must match exactly across all three places.


5. Desktop file

*ilename:*dev.koder.<slug>.desktop *nstall 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
  • Name is TitleCase(slug) per §1.0 (display-name parity rule). The

    override in koder.toml [package].display_name wins when present.

  • GenericName is the function in 2–3 words ("App Hub", "Web Browser",

    "Audio Recorder"). Optional but recommended for KDE/GNOME accessibility.

  • Comment carries the longform `"Koder DisplayName — <oneliner>"`

    so launcher search keeps matching "koder" without showing redundant text on the tile.

  • Keywords always includes koder;<slug>; followed by 2–4 functional

    terms, semicolon-terminated.

  • Icon uses the reverse-domain ID so icon themes can scope icons per product.
  • StartupWMClass must match the binary name so the taskbar groups windows correctly.
  • %u passes a URL argument (for deeplink / URIscheme handling).

*con 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-hubkhub), the .deb *ust*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 *ext*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.txtBINARY_NAME "<slug>"
  • [ ] linux/CMakeLists.txtAPPLICATION_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.toml with [platforms], [install], and [install.desktop] (including startup_wm_class and categories)
  • [ ] Compatibility alias if renaming (/usr/local/bin/<old-name> for one release)
  • [ ] prerm cleans /opt/koder-<slug>/ if upgrading from legacy layout

8.1 Installer responsibility

*he 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> reads kpkg.toml from the ZIP, resolves install paths, creates the .desktop file, and installs the icon — no API call needed.
  • khub install <slug> downloads the .kpkg from the Store, then delegates to the same installer.Install() library path — identical behavior.
  • Metadata like StartupWMClass and XDG Categories come from [install.desktop] in kpkg.toml, ensuring they are consistent regardless of which tool performs the install.

Apps must *ot*bundle their own installer scripts for desktop integration; the kpkg installer library handles it for all platforms.


9. Pure CLI tools (no GUI)

A *ure CLI tool*is a singlebinary Koder utility with no GUI, no DBus 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) *o 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:

  1. *kpkg install slug`*— universal Koder Hub distribution (preferred

    once the module ships a kpkg.toml)

  2. *re-built binary*— cp dev/<slug>/<slug> /usr/local/bin/ for local

    builds during development

  3. *uild from source*— `cd dev/slug && GOWORK=off go build -o slug

    .cmdslug` for contributors

The koder.toml and the README must show all three; the install command in kpkg install is *lways*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 with k if appropriate

  • [ ] koder.toml has slug and binary set to the same string
  • [ ] Go build produces single binary named <slug> (`go build -o slug

    .cmdslug`)

  • [ ] 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 .desktop file, no D-Bus ID, no gtk_application_new
  • [ ] If the CLI has a daemon mode: §6 systemd unit named koder-<slug>.service

10. Multi-binary tooling families

A *ooling 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 *kodertools** (or koderdomain 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 *ever*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 *mitted*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 *ot 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>

Toolingfamily binaries deliberately use the longer `koder prefix instead of the short k`. Two reasons:

  1. *isambiguation from products.*klock could plausibly be a future Koder

    product (a password manager?); koder-lock clearly signals "internal Koder Stack tooling, not an end-user app."

  2. *erbshaped names.*Toolingfamily binaries are verbs (lock,

    stackdoc, commit, grep, check); product binaries are nouns (store, mail, dek). The koder-<verb> form reads naturally as "koder, do X."

Enduserfacing 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 short k<verb> aliases
  • [ ] Top-level koder.toml lists 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

*pdated 20260511* derives from the registry binary name (aliases[0] || slug, per §1), not from a separately-defined "short slug".

applicationId  =  dev.koder.<binary>

where *binary** is the registry-derived binary name (aliases[0] || slug) with any - replaced by _`:

Registry slug aliases[0] <binary> applicationId
koder-eye keye keye dev.koder.keye
koder-kruze koder-kruzekoder_kruze dev.koder.koder_kruze
koder-pass kpass kpass dev.koder.kpass
koder-dek koder-dekkoder_dek dev.koder.koder_dek
koder-mail (kmail) kmail kmail dev.koder.kmail
koder-hub khub khub dev.koder.khub
koder-term kterm kterm dev.koder.kterm
kode kode dev.koder.kode

For components without an alias, the kebab slug is used directly (with -_ for Java package naming). 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 *ust 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 *ay*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) *ust*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 *ot 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, 20260428)

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:

  1. Update koder.toml [android].application_id
  2. Update build.gradle.kts namespace and applicationId
  3. Move Kotlin source under app/src/main/kotlin/dev/koder/<new>/
  4. Backfill the catalog DB row's package_name
  5. Bump version + ship release with *oth*the old and new

    applicationId for one cycle (Android does not allow renaming an applicationId of an installed app — users must reinstall, so the old package is archived in the catalog and the new one becomes the active product)

  6. Open follow-up ticket per product

Until each migration ships, the catalog row's package_name reflects the *urrent*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.toml has [android].application_id declared and matches §11.1
  • [ ] android/app/build.gradle.kts namespace and applicationId match
  • [ ] KotlinJava source rooted at `appsrcmainlangdevkodershort_slug`
  • [ ] Catalog API row has package_name populated and matches koder.toml
  • [ ] Client consumers read package_name from API — no client-side

    heuristic on slug → 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 at specs/icons/generation-targets.kmd covers icon generation)

Source: ../home/koder/dev/koder/meta/docs/stack/specs/binaries-and-cli/naming.kmd