Pular para o conteúdo

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

Todos os triggers

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 .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

Updated 2026-05-11 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 canonical slug (koder-term, kebab-case, registered) from the binary 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 kebab-case identifier (e.g. koder-term)
  • aliases — optional compact forms (e.g. [kterm])

The binary name and kpkg install argument derive as:

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

Worked examples:

ComponentRegistry slugaliases[0]Binary nameInstall command
Koder Termkoder-termktermktermkpkg install kterm
Koder Hubkoder-hubkhubkhubkpkg install khub
Koder Kodakodakodakpkg install koda
Koder Toolskoder-toolsktoolsktoolskpkg install ktools
Koder Kdedupkdedupkdedupkpkg install kdedup

The binary name is never 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 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:

SlugDisplayName
kruzeKruze
ktermKTerm
kmailKMail
kdekKDek
dekDek
mosaicMosaic
hubHub
kodeKode
koder-toolsKoder 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.

ProductMonorepo pathSlugBinary / CLI command
Koder Hubproducts/dev/hub/khubkhub
Koder Mailproducts/horizontal/kmail/kmailkmail
Koder Jetinfra/jet/kjetkjet
Koder Driveproducts/horizontal/drive/kdrivekdrive
Koder Dekproducts/horizontal/dek/kdekkdek
Koder Termproducts/dev/kterm/ktermkterm

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.

CLIMonorepo pathSlugBinaryInstall
Koder Iconproducts/dev/kicon/kiconkiconkpkg install kicon
Koder Dedupdev/kdedup/kdedupkdedupkpkg install kdedup
Koder Shelllinux/shell/koshkoshkpkg 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.

FamilyMonorepo pathBinariesInstall
Koder Toolsdev/koder-tools/koder-lock, koder-stackdoc, koder-pathspec-commit, koder-backlog-grep, koder-hub-checkkpkg 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

Updated 2026-05-11: <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.

Legacy 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 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
  • 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 long-form "Koder <DisplayName> — <one-liner>" 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 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-hubkhub), 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.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

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> 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 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:

  1. kpkg install <slug> — universal Koder Hub distribution (preferred once the module ships a kpkg.toml)
  2. Pre-built binarycp dev/<slug>/<slug> /usr/local/bin/ for local builds during development
  3. Build from sourcecd 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 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> ./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 .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 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:

  1. Disambiguation 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. Verb-shaped names. Tooling-family 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."

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 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

Updated 2026-05-11: 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 slugaliases[0]<binary>applicationId
koder-eyekeyekeyedev.koder.keye
koder-kruzekoder-kruzekoder_kruzedev.koder.koder_kruze
koder-passkpasskpassdev.koder.kpass
koder-dekkoder-dekkoder_dekdev.koder.koder_dek
koder-mail (kmail)kmailkmaildev.koder.kmail
koder-hubkhubkhubdev.koder.khub
koder-termktermktermdev.koder.kterm
kodekodedev.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 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:

ProductSlugCurrent applicationIdCanonical (§11.1)Migration
Koder Hubkoder-hubdev.koder.koder_storedev.koder.khubTODO
Koder Mosaickoder-mosaicdev.koder.koder_mosaicdev.koder.kmosaicTODO
Koder Termktermdev.koder.ticsigndev.koder.ktermTODO
Koder Mailkoder-maildev.koder.kmaildev.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 both 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 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.toml has [android].application_id declared and matches §11.1
  • android/app/build.gradle.kts namespace and applicationId match
  • Kotlin/Java source rooted at app/src/main/<lang>/dev/koder/<short_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)

Referências