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 viaserver/deploy/install.sh(systemd unit). Behind Koder Jet atkterm.koder.dev. - Tag pattern:
products/dev/kterm/v*(perpolicies/releases.kmd). - Binary name:
kterm(perspecs/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/perCLAUDE.mddirectory 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=ktermto 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-keyre-seals every stored credential under a fresh master key (crash-safe via amaster.key.oldretired-key archive);vault-backup -file X.tar.gz/vault-restore -file X.tar.gz [-force]snapshot/restore the vault dir.KODER_KTERM_VAULT_BACKENDselects the master-key source (filedefault;kms/keyringblocked 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 rejectsstep_up_requiredwith 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, zshZDOTDIR, 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:/procon 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) —
UpdateServicedrives the SDKKoderUpdater(koder_kit#072): 6h periodic re-check →KoderUpdatebanner, in-app download → fail-closed SHA-256 verify → per-platform apply, and resume of a postponed staged update on next launch. Surfaced byKtermUpdateBannerat the top of the terminal screen. - Settings on canonical KoderSettingsPage (#024 Phase 3) —
PreferencesScreenwas 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 (#053D3): Fonts (FontEditorScreen), Colors (ColorSchemeScreen), Scrollback (ScrollbackScreen), Profiles (ProfilesScreen), Keybindings (KeybindingsScreen). Theme picker = canonicaldropdown;ColorSchemePresetlives inmodels/. The dialog chrome + snapshot-cancel/restore lifecycle are preserved. Net −1102 LoC. - Debug-soak perf instrumentation (#005) — a
--debug-soak/KTERM_DEBUG_SOAK=1mode (
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 (the003 floor, timed in
PtyService.flush— stopwatch only starts when enabled),flood byte-rate episodes, and Flutter
FrameTimingbuildrasterjank (the #004 paint cost, viaSchedulerBinding.addTimingsCallback— no xterm fork). Rolls up to~/temp/kterm-soak/digest.json(µs histograms, p50p99p999) and mirrors the byte counter intoKoderTelemetry(koder_kterm_pty_bytes_total). Purpose: an AI-driven evidence-soak loop — the owner daily-drives kterm, afires_onalert (kterm-soak-collect.md) tells the AI to read the digest and settle the003 S3 / #004
evidence-soakgates with real numbers instead of a manualreport. Proves the loop on kterm first; graduation to the SDK + a Stack-wide pattern is gated on it settling a gate.