Koder App Behaviors

koder-app specs/koder-app/behaviors.kmd

Comportamentos obrigatórios para todo app Koder (qualquer linguagem/ plataforma/canal): auth (Koder ID), branding, telemetria, auto-update, IPC, error reporting, i18n. Apps não-conformes não podem usar marca Koder, listar na Store ou integrar ao ecossistema. SDKs (koder_kit, engines/sdk/go, etc.) implementam o "how"; este spec é o "what".

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Koder App Behaviors — Spec v0.1

Overview

Every Koder app — regardless of language, platform, or distribution channel — must implement the behaviors defined in this spec. Language/platform SDKs (koder_kit for Flutter, engines/sdk/go, engines/sdk/rust, std.koder.app for Koder Koda, etc.) are the how; this spec is the what.

An app that does not implement all MUST behaviors is not a compliant Koder app and may not use the Koder brand, be listed in the Koder Hub, or integrate with the Koder ecosystem.


1 — Authentication (Koder ID)

1.1 — MUST: authenticate via Koder ID

Every app that has a user-facing UI must support sign-in via Koder ID (OIDC). No proprietary auth system, no separate credentials.

  • The app requests the openid profile email scopes minimum
  • The app stores the access token securely (Keychain on iOS/macOS, Keystore on Android, libsecret / Credential Manager on Linux/Windows)
  • The app refreshes the token automatically using the refresh token before expiry

1.2 — MUST: propagate auth token via IPC

When another Koder app requests the auth token via the auth_token IPC method, the app must return the current user's access token if the caller is a Koder app (verified by signature).

1.3 — SHOULD: support single sign-on across apps

If another Koder app on the same device is already authenticated, the app should reuse the existing session rather than prompting the user to sign in again. Implementation: call auth_token via KoderIPC to the first available Koder app before opening the sign-in flow.


2 — Telemetry

2.1 — MUST: emit structured logs

All log output must be structured (JSON) and sent to the Koder observability stack via the SDK (Go: client.Log, Dart: KoderTelemetry, etc.). Plain print/console.log to stdout is acceptable only during development.

Log levels: debug, info, warn, error, fatal.

2.2 — MUST: trace user-visible operations

Every operation that is user-visible and takes >100ms must be wrapped in a trace span. Examples: app startup, file open, network request, IPC call.

2.3 — SHOULD: emit business metrics

Domain-specific metrics (e.g., files opened, messages sent, playback sessions started) should be sent as gauge/counter metrics. This feeds billing and product analytics.

2.4 — MUST NOT: log PII

Logs and traces must not contain personal data: names, emails, phone numbers, document numbers, file contents, message bodies. Use opaque IDs instead.


3 — Error Handling

3.1 — MUST: comply with error message spec

Every error shown to a user must comply with specs/errors/user-facing-messages.kmd:

  • Humanized text in pt-BR (or en-US for international builds)
  • "Ver detalhes" / "See details" expandable section with the raw technical error
  • Unique error ID in format <PRODUCT>-<CAT>-<CODE>-<SEQ> for support correlation

3.2 — MUST: never show raw stack traces to users

Stack traces, internal file paths, and database error messages must be hidden behind the "Ver detalhes" toggle. The user-facing message must always be a human sentence.

3.3 — MUST: log all errors

Every caught error must be logged via the telemetry SDK with the error ID, the raw error, and the context (screen, operation, user ID hash).


4 — Updates

4.1 — MUST: expose an "Automatic updates" toggle in Settings

Every Koder app that has a user-facing Settings screen must expose an "Automatic updates" / "Atualizações automáticas" toggle. This applies to all UI types: Flutter mobile, Flutter desktop, TV apps (JS/React), and web apps with a persistent settings surface.

Default ON: the toggle must be active by default on every fresh install. The implementation must treat the absence of a stored preference as true (enabled) — the key is only written when the user explicitly toggles it off:

// Dart / Flutter
final prefs = await SharedPreferences.getInstance();
final enabled = prefs.getBool('auto_update') ?? true;  // absent == on
// Web / TV (JS)
const enabled = localStorage.getItem('auto_update') !== 'false';  // absent == on

The toggle lives in Settings under a "General" or top-level "Updates" group. Label and placement must follow specs/koder-app/behaviors.kmd §9 (i18n: en-US default + follow device locale when implemented).

4.2 — MUST: check for updates on startup when auto-update is enabled

When auto-update is enabled (or absent, per §4.1), the app must query the Koder Hub update endpoint on each launch, throttled to at most once per 24 h using a stored last_update_check timestamp.

Endpoint: GET https://hub.koder.dev/api/v1/apps/<slug>/latest

Response:

{ "version": "1.5.0", "min_supported": "1.2.0", "changelog_url": "..." }

When auto-update is explicitly disabled by the user, skip the check entirely — do not even make the network request.

4.3 — MUST: prompt for mandatory updates (regardless of toggle)

If the running version is below min_supported, the app must block usage and show an update prompt. The user may not dismiss this prompt. This rule is not gated on the auto-update toggle — mandatory updates always apply.

4.4 — MUST: download and apply optional updates automatically when enabled

When auto-update is enabled and a newer non-mandatory version is available:

  1. Download silently in the background (use dio + CancelToken on Flutter; fetch + AbortController on web/TV).
  2. Verify checksum before applying (SHA-256 from the Store API response).
  3. Prompt for confirmation — trigger the platform installer with the downloaded artifact; the OS enforces explicit user consent (e.g. Android PackageInstaller, macOS Gatekeeper, Linux dpkg). Never apply silently.
  4. Show subtle progress in Settings while the download is in progress ("Downloading update…" / "Baixando atualização…").
  5. Cancel on toggle-off — if the user disables the toggle while a download is in progress, cancel it via the CancelToken / AbortController.

When auto-update is disabled, only show a dismissible banner (§4.5) — no background download.

4.5 — SHOULD: show optional update banner when auto-update is disabled

When auto-update is disabled and a newer version is available (checked only on manual "Check for updates" action in Settings), show a dismissible banner or in-app notification with a link to the Koder Hub page for the app.

4.6 — SDK-first check for update logic

Before implementing update logic locally, check engines/sdk/koder_kit for a KoderUpdater widget/service that exposes an enabled parameter. If the SDK covers the pattern, wire through it. If enabled is missing, extend the SDK rather than duplicating the logic in the app.

4.7 — Deterministic audit checks

/k-housekeep audit verifies per app:

  1. Settings screen contains a widget/key referencing auto_update preference.
  2. prefs.getBool('auto_update') ?? true (or equivalent absent-means-on pattern) is the sole read point — no hard-coded false default.
  3. Update check is gated on the preference value before the network call.
  4. Mandatory-update path (min_supported) is NOT gated on the preference.

5 — Deep Linking

5.1 — MUST: register the koder-<slug>:// URL scheme

Every app must register its URL scheme on all platforms it targets:

  • Android: <intent-filter> in AndroidManifest.xml
  • iOS: CFBundleURLSchemes in Info.plist
  • Desktop: .desktop file URI handler (Linux), LSApplicationQueriesSchemes (macOS), registry key (Windows)

koder-<slug>://open?uri=<encoded-uri> must open the specified resource in the app. Unrecognized URIs return error -32601.

Apps should be able to produce deep links to other Koder apps. Example: Drive producing koder-dek://open?uri=<audio-uri> to open a file in Dek.


6 — IPC

6.1 — MUST: implement the KoderIPC protocol

Every app must implement the KoderIPC protocol as specified in specs/ipc/protocol.kmd:

  • Start an IPC server at the platform-appropriate transport address on launch
  • Respond to capabilities and ping
  • Stop the server on clean shutdown

6.2 — MUST: declare capabilities at startup

The app must publish its capability list immediately when a client calls capabilities. The list must be static (not change at runtime without restarting the IPC server).

6.3 — MUST NOT: accept undeclared methods

Any method not in the declared capability list must return error -32601.


7 — Branding

7.1 — MUST: use the Koder design system

Apps must use the Koder color tokens, typography, and spacing system as defined in the brand assets. No custom primary colors or fonts without explicit design review.

7.2 — MUST: support light and dark themes

All Koder UIs — web apps, landing pages, Flutter mobile, Flutter desktop, TV apps — must comply with specs/themes/light-dark.kmd in full. Key requirements:

  • First launch / no saved preference: open in the theme that matches the OS setting. Never hard-code dark or light as a fixed default.
  • User choice persistence: save to localStorage (web) or SharedPreferences (Flutter) under key "theme" ("light" / "dark"). Saved choice overrides OS preference on subsequent launches/visits.
  • OS preference propagation: when no preference is saved, follow live OS changes (matchMedia on web, WidgetsBindingObserver.didChangePlatformBrightness on Flutter).
  • Anti-flash: apply the theme synchronously before the first render (inline <head> script on web; read preference in main() before runApp() on Flutter).

SDK: KoderTheme widget from engines/sdk/koder_kit (planned v0.6.0); engines/sdk/koder_web_kit for web. Full implementation patterns in specs/themes/light-dark.kmd.

7.3 — MUST: display product name in title bar

Per specs/desktop-apps/title-bar.kmd: desktop apps display the product name (not "Koder") in the window title bar.


8 — Identifiers

8.1 — MUST: use the canonical slug

Every app has a canonical slug (e.g., dek, drive, kmail, pass). This slug is used:

  • As the IPC address: socket/pipe name, D-Bus name (dev.koder.<slug>)
  • As the deep link scheme: koder-<slug>://
  • As the binary name: k<slug> (per specs/binaries-and-cli/naming.kmd)
  • As the Store ID and telemetry service field

9 — Localisation (i18n)

9.1 — Default language is en-US

All Koder apps ship with English (en-US) as the default and baseline language. All source strings are written in en-US. See policies/language.kmd for full policy.

9.2 — MUST: follow device/browser locale when i18n is implemented

Once multi-language support is formally added to a product, the displayed language must follow the device or browser locale automatically — never require the user to configure it manually on first launch.

  • Flutter: use flutter_localizations + intl; MaterialApp(localizationsDelegates: ..., supportedLocales: ...) resolves the locale from the OS automatically.
  • Web (app/landing): use navigator.language (browser) or Accept-Language header (server-rendered) as the initial locale.
  • TV (JS/React): same as Web — navigator.language.
  • Fallback: always fall back to en-US when the device locale has no translation.

9.3 — MUST: use the SDK localisation helper

Use KoderL10n from engines/sdk/koder_kit (Flutter, shipped v0.16.0+), KoderL10n from engines/sdk/koder_web_kit (JS/Web, shipped v0.3.0+), or the per-surface equivalent (engines/sdk/go/l10n for Go services / CLIs). Centralise all user-visible strings in the canonical files per surface (ARB for Flutter, JSON for Web/TV, Go map literals for server / CLI / TUI). Inline string literals in widget / template code are forbidden — see T1 in specs/i18n/contract.kmd.

9.4 — MUST NOT: mix locale-selection and locale-display concerns

The locale selector lives in Settings, not in the onboarding flow. On first launch, the app silently follows the device locale — no locale-picker dialog is shown. The canonical UI for the selector is KoderLanguagePicker (Flutter) / <koder-language-picker> (Web) — see specs/i18n/contract.kmd §R3 for per-surface placement (Settings tile, landing-page footer, CLI flag, TUI shortcut, etc.).

Cross-reference: specs/i18n/contract.kmd is the canonical cross-surface i18n spec — covers persistence (R2), switcher position (R3), key naming (R6), supported locales (R7), per-surface string-file paths (R8), and the TDD test contract (T1-T6). This §9 is intentionally a summary; new behaviour or per-surface details land in specs/i18n/.


File: meta/docs/stack/specs/koder-app/behaviors.kmd

Trigger: read this spec before creating any new Koder app or SDK, or before auditing an existing app for ecosystem compliance.

CLAUDE.md trigger entry to add: | Criar qualquer novo app Koder ou binding SDK | specs/koder-app/behaviors.kmd |

Referências