Koder Term — `products/dev/kterm/app`

Cross-platform tiling terminal emulator with persistent sessions, SSH multiplexing, image clipboard, and AI-CLI integration.

Role in the stack

Area Sector Consumers
products/dev terminal end-users (developers); other Koder dev tools (Kdev IDE for embedded terminal panes — planned); claude-tiles runbook (8 persistent Tilix tiles + Claude sessions)

Primary couplings

Module Nature
engines/sdk/koder_kit Flutter SDK — tema, layout, navigation, error reporter, update service, sign-in, settings tiles. First consumer of KoderUIStylePicker (v0.10.0, ETAPA 4.3).
engines/sdk/koder_ipc Koder IPC SDK — D-Bus service integration (lib/services/dbus_service.dart), replacing the legacy raw D-Bus client (ticket #025, done). #055 added the kterm.sendText verb (send-keys into a pane, addressed by the KTERM_PANE env; the terminal-emulator analog of tmux send-keys) + wired the production onAction dispatch that #025 had only ever set in tests.
services/foundation/id Koder ID — OIDC auth for web access (kterm.koder.dev). Sessions are private and per-user.
services/foundation/reporter Error reporting backend — wired via KoderErrorReporter (ticket #010 pending).
services/foundation/keys KoderKeys — session-encryption key management for persistent storage at rest (ticket #018 pending).
services/foundation/katalog Hub depot — kpkg signing + asset registry for releases (depends on katalog #013 deploy).
infra/net/jet Reverse proxy at kterm.koder.dev (Koder Jet, sites.toml on s.forge).
infra/observe Telemetry pipeline — traces + counters via KoderTelemetry (ticket #017 pending).
xterm (third-party) Terminal emulator widget for Flutter desktop.
flutter_pty (third-party) PTY allocation on LinuxmacOSWindows.
dartssh2 (third-party) SSH client + multiplexer.

Status

v1.32.4 (2026-05-29) — in development. Desktop (LinuxmacOSWindows) and web (browser via Go server bridge); mobile: Android signed-release pipeline live (own vault key, v1.32.6); iOS scaffold exists, pending a macOS runner (#026).

Feature Status
Tiling layout (split HV2x2, drag-drop)
Geometric directional pane navigation (Alt+arrows, spatial at any nesting depth) ✅ #2043
Directional pane swap (Ctrl+Shift+arrows, tmux swap-pane semantics) ✅ #2044
Persistent sessions (survive disconnect/restart)
Image clipboard (paste screenshot → ~/temp/, auto-insert path)
Web access (kterm.koder.dev via Go server)
Koder ID OIDC auth (web)
AI CLI detection (Claude Code / Codex / Gemini / Aider)
Profiles (fonts, colors, cursor, scrollback, triggers, badges)
SSH client + multiplexer
Quake mode (F12 toggle visibility)
Auto-update toggle (KoderUpdateService) ✅ #009
Hub multi-platform release pipeline ✅ #014
Sessions encryption at rest ✅ #018
Web server rate limiting ✅ #019
Bastion (SSH via vault + targets) ✅ #031
Audit (session recording) ✅ #032
Process stats chip (Linux local) ✅ #041
Process stats chip (remote SSH via bridge) ✅ #045
QR session sharing (LAN direct, Phase 1) ✅ #046
Command-finished sound (OSC 133 shell integration; opt-in, off by default) ✅ #051
Web client server-side prefs (scrollback + auto-update/error-report toggles) ✅ #050
Web client SplitNode tiling tree + drag-resize (arbitrary depth) ✅ #049 (via #029)
Binary naming spec-compliance (pilot) ✅ #040
Settings SDK migration Phase 3 ⏳ #024
Bastion Phase 3 (MFA step-up) ⏳ #033
Bastion Phase 4 (KMS master key) ⏳ #034
Bastion Phase 5 (SaaS quotas + sharing) ⏳ #035
Process stats macOS/Windows ⏳ #042
Android signed-release pipeline (own vault key) ✅ #026 (v1.32.6)
iOS build pipeline (needs macOS runner) ⏳ #026

Architecture

Desktop App (Flutter)
  └── PTY → Local shell
  └── SSH → ControlMaster → kterm-bridge (process stats, clipboard, file xfer)

Web Browser (xterm.js)
  └── WebSocket → Go Server → PTY → Shell
                    ├── Session persistence (~/.koder-kterm/sessions/)
                    ├── Koder ID OIDC auth
                    ├── Bastion (SSH vault + targets)
                    └── QR session sharing (#046)
                          └── /ws/share (token-auth WebSocket)
                          └── /api/share/qr (QR PNG via go-qrcode)

Mobile App (Flutter — scan QR)
  └── kterm:// URI → WebSocket → Go Server (shared session)

Three surfaces, one product: the Flutter desktop app talks directly to a local PTY (or SSH via ControlMaster); the web browser talks WebSocket to a Go server (server/) that owns the PTY and persists session state; mobile scans a QR code to join a shared session on the Go server. SSH tiles get remote process stats via the kterm-bridge daemon.

Repository layout

products/dev/kterm/
├── app/                   ← Flutter (mobile/desktop/web)
│   ├── lib/
│   │   ├── main.dart, app.dart
│   │   ├── screens/       ← preferences, profile_editor, terminal
│   │   ├── widgets/       ← tiling, terminal, ssh, paste, session
│   │   ├── services/      ← pty, ssh, ssh_multiplexer, clipboard,
│   │   │                    notification, storage, search, trigger,
│   │   │                    ai_integration, image_protocol, dbus,
│   │   │                    quake, profile_switch, process_stats,
│   │   │                    share (#046)
│   │   ├── providers/, models/, theme/, l10n/, core/
│   │   ├── android/, ios/, linux/, macos/, windows/
│   │   ├── server/        ← Go web server bridge
│   │   ├── landing/       ← kterm.koder.dev product landing
│   │   └── docs/, assets/
│   └── backlog/           ← pending/in-progress/done tickets
└── bridge/
    └── cmd/kterm-bridge/  ← Go bridge binary
    └── install/

Releases

  • Hub depot: currently linuxx86_64 only; macOSWindows/web pending

    (#014).

  • Server bundle: Go binary built from server/, deployed via

    server/deploy/install.sh (systemd unit). Behind Koder Jet at kterm.koder.dev.

  • Tag pattern: products/dev/kterm/v* (per policies/releases.kmd).
  • Binary name: kterm (per specs/binaries-and-cli/naming.kmd,

    aliases[0] from registries/component-names.md). Piloted in #040.

  • CHANGELOG: app/CHANGELOG.md.

Operational notes

  • claude-tiles integration — kterm is the canonical host for the

    8-tile persistent Claude Code session runbook (infrastructure/claude-tiles.md).

  • Image clipboard convention — images saved to ~/temp/ per

    CLAUDE.md directory rule (avoid /tmp/ tmpfs limit). Cleanup/ rotation pending #012.

  • AI env vars — kterm exports KODER_TERM=1, KODER_TERM_VERSION,

    KODER_TERM_IMAGES=1, TERM_PROGRAM=kterm to all PTYs for AI tools to detect kterm-specific capabilities (Sixel/Kitty image protocol).

  • Credential vault operator commands (#034) — the server binary

    doubles as an offline vault tool: koder-kterm-server rotate-master-key re-seals every stored credential under a fresh master key (crash-safe via a master.key.old retired-key archive); vault-backup -file X.tar.gz / vault-restore -file X.tar.gz [-force] snapshot/restore the vault dir. KODER_KTERM_VAULT_BACKEND selects the master-key source (file default; kms/keyring blocked on a Koder Vault service — fail loudly). Run vault commands with the server stopped.

  • Web client keybindings (#027) — the vanilla-JS web client ships

    rebindable shortcuts (toolbar ⌨ drawer; overrides in localStorage["kterm-keybindings"]), at parity with the desktop set.

  • Bastion MFA step-up (#033) — before opening an outbound SSH session,

    the server requires a fresh MFA assertion via Koder ID GET /v1/auth/step-up/check (id engine#189). The window is account-wide in vault metadata (mfa_window_seconds, default 300) via /api/bastion/stepup (GET status / PUT window). Connect rejects step_up_required with a re-auth URL (prompt=login&max_age=0) when stale/absent; fails closed when Koder ID is unreachable. Gate is disabled for self-host without OIDC. The web bastion drawer shows a "Recent MFA: Nm ago" / "Step-up required" banner + "Step up now".

  • Silent shell integration (#052) — the OSC 133 command-finished hooks

    (#051) install via the shell's own init files (bash --rcfile, zsh ZDOTDIR, fish --init-command) instead of being echoed to the PTY, so nothing lands in the visible buffer or shell history. Unknown shells / remote SSH legs fall back to the visible stdin injection.

  • Tile process stats (#041#042) — the per-tile CPU%RAM chip is fed by

    a pluggable ProcessStatsBackend: /proc on Linux, libproc FFI on macOS, ToolHelp+psapi FFI on Windows. CPU is normalized to cumulative µs so the rate math is platform-uniform. (mac/win runtime validation gated on those hosts.)

  • Hub-driven auto-update (#020) — UpdateService drives the SDK

    KoderUpdater (koder_kit#072): 6h periodic re-check → KoderUpdate banner, in-app download → fail-closed SHA-256 verify → per-platform apply, and resume of a postponed staged update on next launch. Surfaced by KtermUpdateBanner at the top of the terminal screen.

  • Settings on canonical KoderSettingsPage (#024 Phase 3) — PreferencesScreen

    was rebuilt from a bespoke master-detail nav-rail to a single canonical-ordered scroll of KoderSettingsGroup/KoderSettingsTile (9 groups). Full-width visual editors are dedicated sub-pages reached by button tiles (#053 D3): Fonts (FontEditorScreen), Colors (ColorSchemeScreen), Scrollback (ScrollbackScreen), Profiles (ProfilesScreen), Keybindings (KeybindingsScreen). Theme picker = canonical dropdown; ColorSchemePreset lives in models/. The dialog chrome + snapshot-cancel/restore lifecycle are preserved. Net −1102 LoC.

  • Debug-soak perf instrumentation (#005) — a --debug-soak / KTERM_DEBUG_SOAK=1

    mode (services/soak_probe.dart, OFF by default, ships in the release binary) records performance signals only, never terminal content (observability-first R8.1 / instrumentation-contract C5): VT parse latency per coalesced write (the

    003 floor, timed in PtyService.flush — stopwatch only starts when enabled),

    flood byte-rate episodes, and Flutter FrameTiming buildrasterjank (the #004 paint cost, via SchedulerBinding.addTimingsCallback — no xterm fork). Rolls up to ~/temp/kterm-soak/digest.json (µs histograms, p50p99p999) and mirrors the byte counter into KoderTelemetry (koder_kterm_pty_bytes_total). Purpose: an AI-driven evidence-soak loop — the owner daily-drives kterm, a fires_on alert (kterm-soak-collect.md) tells the AI to read the digest and settle the

    003 S3 / #004 evidence-soak gates with real numbers instead of a manual

    report. Proves the loop on kterm first; graduation to the SDK + a Stack-wide pattern is gated on it settling a gate.