DE

Architecture (Developer)

korTTY is a modular JavaFX SSH client built on a layered architecture. This guide describes the core modules, their responsibilities, dependencies, and how they interact to deliver a seamless terminal experience with advanced features like job scheduling, AI integration, and terminal effect plugins.

Startup & manager init

Architecture Overview

The following diagram shows the main components of korTTY and how they relate:

Architecture

Modules and Dependencies

KorTTY is organized into distinct functional modules. The diagram below groups them by function:

Modules overview

Module Breakdown

Module Purpose Key Classes
core SSH connectivity, shared interactive host-key trust, session management, AI integration, terminal automation SshTtyConnector, SshHostKeyTrustManager, AiChatManager, TerminalAgentService, Mosh4jTtyConnector
ai Signed model/prompt catalog, Hugging Face metadata/downloads, embedded llama.cpp and MLX runtimes, and signed runtime packages AiCatalogService, HuggingFaceClient, LlamaRuntimeManager, LlamaRuntimePackageInstaller, EmbeddedMlxAiService, MlxRuntimeLocator
rag Safe source scanning, extraction, chunking, embeddings, vector stores, synchronization, and bounded retrieval RagSourceScanner, RagSourceSynchronizer, LocalHnswStore, RagRuntimeService
ui JavaFX user interface, dialogs, terminal views, SFTP manager TerminalView, TerminalTab, ConnectionEditDialog, SFTPManagerTab, SnippetEditDialog
model Domain objects for connections, credentials, snippets, jobs ServerConnection, StoredCredential, Snippet, JobSchedule
jobscheduler Background job scheduling and execution JobSchedulerService, JobSchedulerJobRunner, JobJournalEntry
security Master password, encryption/decryption, password vault MasterPasswordManager, EncryptionService, PasswordVault
persistence XML serialization, file I/O, repository pattern XMLConnectionRepository, HistoryStorage
plugin Terminal effect plugins (ServiceLoader SPI) TerminalEffectPlugin, TerminalEffectSession
power Activity-aware sleep and App Nap management per platform PowerManagementCoordinator, MacPowerManagementBackend
telemetry Consent-gated anonymous usage events TelemetryService, Telemetry
control Default-off local control API: transport, token, verbs, event bus (see Control API) ControlApiServer, ControlVerbs, ControlSurface, ControlEventBus, ControlApiGate
cli The kortty-cli client that speaks the control API (see Control CLI) KorttyCli
teamwork Collaboration and remote access features Team-based session sharing and coordination
jmx Java Management Extensions monitoring SSHClientMonitor, SSHClientMonitorMBean
codingagent Coding-agent awareness: detects terminal coding agents in panes, tracks their state, and feeds the Coding Agents panel and notifications CodingAgentDetector, CodingAgentMonitor, CodingAgentRegistry, CodingAgentService
isolation Session isolation levels and the sandboxed local shells; SSH sessions run in session worker processes (core.worker) IsolationLevel, IsolationSettings, SandboxBackend, MacSandboxBackend, BubblewrapSandboxBackend, SessionWorkerProcess
paste Paste protection: inspects pasted text and decides whether to confirm, sanitize or pace it PasteGuard, PasteInspection, PasteRules, PastePacer
policy Enterprise policy file (kortty-policy.toml): managed settings, restrictions and encrypted policy values PolicyManager, PolicyLoader, EffectivePolicy, ServerAccessPolicy, PolicyValueCipher
shellintegration Shell integration (prompt marks, command status, notifications) and per-pane activity tracking ShellIntegrationInjection, ShellIntegrationWrapperDirectory, PromptNavigator, CommandBlockStore
perf Opt-in UI timing trace for performance work PerfTrace
platform Platform support for packaged builds, such as Flatpak FlatpakSupport
update Version checking and update notifications Update service and version metadata

SithTermFX Terminal Engine

KorTTY uses SithTermFX 1.2.7 as its primary terminal emulator, built from source during the build process. SithTermFX provides:

  • Terminal emulation: VT100/xterm-compatible terminal rendering powered by a custom JavaFX control
  • OSC 8 hyperlinks: Clickable explicit hyperlinks printed by programs. korTTY replaces SithTermFX's default link handler in every pane with its own allowlist (http, https, ftp, ftps, mailto, strictly parsed, at most 8 KB, no control or bidi characters, mailto limited to recipients, subject, body and in-reply-to) and opens links through JavaFX HostServices. Links open only on a single, still Cmd/Ctrl+click without Alt (TerminalLinkClickPolicy.isFollowLinkGesture, user decision D2). korTTY's settings provider answers SithTermFX's isFollowLinkGesture with that rule, and SithTermFX 1.2.3 follows an OSC 8 link only on a still, single primary click the rule accepts, so a plain click, a double or triple click and a drag release on a link act as on other text, word and line selection included. The KorttyLinkInfo that SithTermFX then navigates hands itself to its pane (KorttyTerminalPanel.followLink), which opens it only when the primary MOUSE_CLICKED filter that TerminalLinkClickPolicy installs on the canvas noted that same click, with the gesture, on that same link, so the gesture holds under any settings provider and no navigation bypasses the checks below. The same filter opens a link korTTY found in plain text itself, by a pure decision table, and consumes that click with a focus request, so the split pane still tracks the focused pane. news: and other targets stay plain text, and a file: target becomes a link only in a pane that opens files as text (see below), so terminal output never reaches java.awt.Desktop.open and cannot launch a local file. Panes keep SithTermFX's default HOVER_WITH_BOTH_COLORS mode, in which linked text keeps its own colors and attributes, including SGR changes inside the link (since SithTermFX 1.2.3), and is underlined while the gesture is held over it, and TerminalScreenRenderer records a link cell with HyperlinkStyle.getUnderlyingStyle(), the style of the text beneath the link. korTTY registers no SithTermFX HyperlinkFilter: links in plain text are found on demand instead, so they cost nothing while output arrives and open only through korTTY's gate; NoHyperlinkFilterGuardTest fails the build if a filter is added. TerminalLinkResolver is the click policy's hit resolver: under the buffer lock it returns the OSC 8 link of the clicked cell, or else runs TerminalLinkDetector for web and e-mail addresses over the logical line around the cell, bounded to ±16 wrapped rows and 4096 characters, and drops a token that may run on past that window instead of opening it cut short. The kinds come from a per-pane supplier that reads GlobalSettings.terminalLinkDetectionEnabled on every click, so detection costs nothing while output arrives. TerminalLinkHoverController resolves the same way only when the mouse reaches another cell: a MOUSE_MOVED handler added after SithTermFX's own sets the canvas cursor explicitly on every cell change (HAND only for a link that can open), draws the underline of a plain-text link as Lines in the pane's LINKS layer (TerminalSplitPane.paneOverlay, an unmanaged, mouse-transparent Pane in the pane wrapper at a negative view order, placed through scene coordinates because the wrapper also holds the timestamp gutter and the agent panel) and shows a tooltip with TerminalLinkOpener.displayTarget (punycode host, percent-encoded controls and bidi characters, shortened to about 200 characters without cutting the host). It tears down on exit, drag, scroll, non-modifier key, pane or window focus loss and scene removal, and while a link is shown it follows a buffer model listener (coalesced onto the FX thread) and canvas resizes to re-resolve. An OSC 8 click whose visible text is a URL on another host than the target (TerminalLinkOpener.visibleHostMismatch) opens only after TerminalLinkMismatchDialog, asked after the click is handled, with Cancel as the default button. The terminal context menu reaches the same links without a modifier: TerminalLinkContextMenu adds a canvas MOUSE_PRESSED filter that resolves the link under every right-button press (any other press forgets it), and TerminalSplitPane.createFullContextMenu puts Open Link (the click's open path, mismatch question included) and Copy Link Address (through KorttyClipboard, so the internal clipboard mode applies; an OSC 8 link's toASCIIString() target, a plain-text address as printed) above the edit entries for a link with a target. Files go the same ways without a browser: TerminalFileLink is a path the detector found (Kind.PATH, split by TerminalPathToken) or an OSC 8 file: target that fromFileUri accepts (absolute path, no // network share, no backslash, no control, bidi or format characters after decoding, a host without user name or port). KorttyOsc8LinkInfoProvider creates a file: link only while the pane's TerminalFileLinkHandler is enabled (asked on the emulator thread), which TerminalView makes true only for SSH and local-shell panes while a TerminalPathOpenHandler is set, and MainWindow sets one only when the policy's loadIntoSnippetEditor is not DENY; the same condition adds PATH to the pane's plain-text link kinds. The pane's shared lookup drops the file of a link the handler does not accept (an OSC 8 host that is not empty, localhost, the connection host, the prompt host or the local host name, by first DNS label), so the hover, the click and the context menu agree. Opening resolves the path with RemoteTextFileSelectionSupport.resolveRemotePath / resolveLocalPath (absolute, ~, and relative against the working directory at click time, which is looked up only for a relative path; UNC and device paths refused before and after resolving) and reads it through the same reader as Open in Snippet Editor, with the foreign-session guard, regular-file and UTF-8 checks and a 10 MB cap for SFTP and local reads that also stops a file growing during the read. A file from an OSC 8 link opens read-only: ExternalFileActionConfig gets no overwrite or save-as action and a lockedReason. TerminalLinkHoverController adds its MOUSE_MOVED handler from KorttyTerminalPanel.init(), after SithTermFX adds its own there, so the cursor korTTY sets wins also over an OSC 8 link it does not open.
  • Quick select: Edit → Quick Select (Cmd/Ctrl+Shift+Space) labels what the focused pane shows. The chord is a MainWindow constant routed through the scene shortcut router (scope: a terminal tab with the keyboard focus inside it; residue: space or NUL) and set on the menu item, which is disabled outside terminal tabs. QuickSelectScreen reads the visible rows at the panel's scroll origin under the buffer lock, joins wrapped rows within the viewport (sliced to the detector's 16 KB input cap) and runs TerminalLinkDetector for every kind, dropping a token that runs on past the viewport; the user's own patterns (QuickSelectPatterns, from GlobalSettings.terminalQuickSelectPatterns: at most 16 of at most 512 characters, compiled once per change of the list) run over the same scanned text and come out as Kind.CUSTOM, each through a DeadlineCharSequence with 10 ms per line and 100 ms for all of one capture, a pattern that overruns being skipped for the rest of the capture, and a pattern match replaces the built-in matches it overlaps when it is at least as long as each of them; QuickSelectLabels assigns prefix-free labels from the user's alphabet (terminalQuickSelectAlphabet, validated by alphabetProblem, the default when unusable; one letter per text up to the alphabet's size, otherwise two) bottom row first, one per distinct text; QuickSelectSession is a pure state machine over KeyCode and Shift (modifier keys and the starting chord are ignored, a label copies, Shift on its last letter opens, Backspace edits the prefix, Escape and any other key cancel). TerminalQuickSelectController installs the split pane's first KEY_PRESSED, KEY_TYPED and INPUT_METHOD_TEXT_CHANGED filters (before TerminalView's own, so before the agent input lock and the per-pane broadcast mirror): while a session runs every key, typed character and input-method event is consumed, and QuickSelectInputGuard reuses KeyTypedResidueGuard to swallow the last key's KEY_TYPED once plus any input-method text until the next key press. It draws Rectangle boxes and inline-styled Label chips (pane colors inverted, terminal font) into the pane's LINKS layer through scene coordinates (TerminalCellGeometry.boxes), copies through KorttyClipboard, opens a URL, an e-mail address or, where the pane opens files (KorttyTermWidget.opens), a path through the widget's openLink (the Cmd/Ctrl+click path) and copies whatever it does not open, and ends on a mouse press, canvas focus loss, a canvas move or resize, a scroll-bar change, a font-size change (TerminalView.reinitPaneFont) or scene removal; a buffer model listener, coalesced onto the FX thread, drops the matches whose cells no longer hold their captured text and ends the session when none is left.
  • Session integration: Direct JAXB marshaling of terminal state for session recording and replay
  • Color support: Configurable ANSI and TrueColor handling with per-connection overrides
  • Scrollback memory limit and compression: Since SithTermFX 1.2.6 a pane's history is bounded by an estimate of its heap as well as by its line count. KorTTYSettingsProvider answers getBufferMaxMemoryBytes() and isScrollbackCompressionEnabled() from ConnectionSettings.scrollbackMemoryMb and scrollbackCompression, and TerminalView.applyScrollbackMemory hands a saved change to every open pane's TerminalTextBuffer. With compression on, a low-priority SithTermFX thread packs history lines into deflated blocks of up to 128 lines once a pane has had no output for 1.5 s, holding the buffer lock for a few milliseconds at a time; the rows on screen with a screenful around them, the newest screenful and lines with a custom highlighting stay unpacked, and nothing is packed while find results are shown. A packed line keeps its TerminalLine identity, wrapped flag and styles, so ScrollbackTrimTracker, the command marks and the keyword highlighter, which all anchor on line identity, are unaffected; drawing a line or reading its entries unpacks its block, getText() decodes without unpacking
  • Hyperlink boundary fix: Hyperlink hit-testing rejects the non-existent row at line == height, preventing bottom-row TerminalTextBuffer range errors. korTTY carried this as a pinned patch until it shipped upstream in SithTermFX 1.2.3; TerminalPanelBoundaryPatchTest pins the behaviour
  • Shortcut-chord fix: Shortcut-chord KEY_TYPED characters (for example Cmd+Shift+D) no longer reach the pty or broadcast panes. Carried as a korTTY patch until SithTermFX 1.2.3; TerminalPanelShortcutKeyTypedPatchTest pins the behaviour
  • Control-sequence bounds fix: The 1024-character push-back through which ControlSequence re-reads the characters it cannot place in a CSI ahead of the sequence is bounded: stray characters and then whole parameters that do not fit are dropped, and the re-emitted sequence always keeps its final character, so an overlong sequence in remote output no longer throws ArrayIndexOutOfBoundsException on the emulator thread and ends the session. OscEventSplitter replays the same stray characters. Carried as a korTTY patch until it shipped upstream in SithTermFX 1.2.3 (vendor-patches/0003-control-sequence-push-back-bounds.patch); ControlSequenceBoundsPatchTest pins the behaviour
  • Scroll-region correctness: The scrolling region does not confine the cursor while origin mode (DECOM) is off. Text addressed above the top margin or below the bottom one stays on that line instead of being pulled into the region, and only a line feed or autowrap that actually crosses the bottom margin scrolls — so tmux's first pane row, its status line and the cursor all land where the application put them. Carried as a korTTY patch until it shipped upstream in SithTermFX 1.2.2

Build Integration

The build process automatically:

  1. Clones SithTermFX at tag v1.2.7 into vendor/sithtermfx (no GitHub token required)
  2. Builds it locally using Maven via the installSithtermfxLocal task — the released tag as is, since korTTY carries no SithTermFX patches
  3. Installs artifacts to the local Maven repo (mavenLocal()); the task runs again whenever the core or UI JAR of the pinned version is missing there
  4. Links SithTermFX core and UI modules into the korTTY JAR

No network access is needed after cloning; all build steps are deterministic and reproducible.

Persistence and Configuration

KorTTY stores its main configuration, credentials, and session state under ~/.kortty/, primarily as JAXB XML. Local-model registration also uses JAXB XML, while knowledge-store configuration is strict JSON and the local vector graph is a regenerable binary snapshot. This approach ensures:

  • Portability: Easy manual inspection and migration across systems
  • Encryption: Sensitive data (passwords, SSH key passphrases, master password hash) is encrypted with AES-256-GCM
  • Atomic writes: File updates use temporary files and atomic rename to prevent corruption on crash
  • No required database: Local HNSW is self-contained; Qdrant is an optional second vector backend

Configuration File Structure

~/.kortty/
├── connections.xml              # Saved SSH/Mosh connections
├── credentials.xml              # Stored usernames/passwords for env-specific targets
├── ssh-keys.xml                 # SSH key references and encrypted passphrases
├── gpg-keys.xml                 # GPG key management for backup encryption
├── global-settings.xml          # Application-wide settings (theme, language, AI profiles)
├── llm/
│   ├── models.xml               # Local GGUF registrations and typed launch settings
│   ├── models/                  # Managed GGUF weights (regenerable; excluded from backup)
│   ├── runtime/                 # Versioned native llama.cpp packages
│   ├── catalog/                 # Regenerable signature-verified catalog cache
│   └── run/                     # Temporary sidecar key files and logs
├── rag/
│   ├── stores.json              # Knowledge-store and source configuration
│   └── stores/                  # Regenerable local HNSW snapshots
├── job-scheduler.xml            # JobScheduler jobs, host-key pins, sudo secrets, journal
├── ssh-host-keys.properties     # Shared interactive Terminal/SFTP/Mosh host-key pins
├── terminal-effect-plugins.disabled # Disabled terminal-effect plugin IDs (one per line)
├── master.key                   # PBKDF2-hashed master password (310,000 iterations)
├── logs/                        # Application log (kortty.log) and its archives
├── history/                     # Project screen snapshots (gzip, one per terminal tab)
├── plugins/                     # Imported external terminal-effect plugin JARs
├── bundled-plugins/             # Runtime copies of bundled exportable plugin JARs
├── projects/                    # Project files (connection sets with saved layout)
├── i18n/                        # Dynamically generated language property files
└── ssh-keys/                    # Optional: copied SSH keys (included in backups)

Serialization and Encryption

JAXB Marshaling converts domain objects to/from XML with automatic schema validation. Key repositories include:

  • XMLConnectionRepository: Manages ServerConnection objects with SFTP, jump server, and tunnel details
  • CredentialManager: Stores environment-specific credential patterns
  • SSHKeyManager: Wraps imported SSH keys with encrypted passphrases
  • JobSchedulerRepository: Persists JobSchedule objects and the execution journal

AES-256-GCM encryption protects sensitive fields:

  • Master password is hashed once on first login, stored as master.key
  • All encrypted fields use AES-256-GCM with random IVs, under a key derived from the master password
  • SSH key passphrases, connection passwords, and sudo secrets are encrypted before persistence
  • Backup archives can be encrypted with password-protected ZIP or GPG

Core Module Organization

SSH and Session Management

Component Responsibility
SshTtyConnector Apache SSHD terminal connection of one pane: jump host, authentication, shell channel and disconnect detection
ActiveConnectionRegistry Live list of the open SSH and Mosh terminal connections (one per pane), read by the JMX MBean
SshTunnelManager The SSH tunnels (-L, -R, -D) of one terminal tab: opened on the tab's first SSH session after login, stopped before a reconnect and opened again afterwards, moved to another pane of the same server when that pane closes; split panes never open their own
SshTunnelApprovals One-time confirmation per connection and tunnel set before tunnels open, stored as a fingerprint in ssh-tunnel-approvals.properties
SshHostKeyTrustManager Shares normalized host:port TOFU pins across interactive Terminal, SFTP, and Mosh bootstrap connections with atomic, cross-process persistence
SSHKeyManager Centralized SSH key storage with encrypted passphrase support
Mosh4jTtyConnector Mosh protocol connector using the mosh4j library (dynamically loaded)
NativeMoshTtyConnector Native OS Mosh support (fallback)
TemporarySSHKeyManager Time-limited SSH keys (e.g., from CyberArk)

AI and Agent Integration

Component Responsibility
AiChatManager Manages AI chat sessions and conversation history
AiServiceFactory Maps a profile to HTTP, Anthropic, local CLI, or embedded llama.cpp transport, then adds prompt presets and optional RAG
AiCatalogService Returns a reverified cache/bootstrap immediately and schedules one signed stable-channel refresh for recommendations and preset-family mappings
LlamaRuntimeManager Shares one isolated authenticated sidecar per compatible GGUF configuration and grants reference-counted request leases
RagAugmentedAiService Retrieves bounded excerpts for ordinary AI actions and adds the untrusted context before the model preset
TerminalAgentService Executes agent workflows: probes SSH session, sends task to model, validates/executes commands
AiInternetAccessConfiguration Configures web tools (Tavily, Brave Search, SearXNG, etc.) per profile
AiChatExportService Exports AI chats to Markdown, PDF, YAML, JSON, XML, Asciidoctor

Job Scheduling

Component Responsibility
JobSchedulerService Main scheduler service; manages scheduled jobs and execution queue
JobSchedulerJobRunner Executes individual jobs (SSH command, script, AI agent, SFTP, Rsync)
JobJournalEntry Run journal entries kept in job-scheduler.xml, with secrets redacted and the configured retention (14 days by default)

Session Journal

Component Responsibility
SessionJournalService Creates and stores one journal per session in journals/: the curated journal.xml and the rotated capture log
SessionJournalExportService Exports a journal to its supported formats
SessionJournalCrossSearchService Searches across all journals, with the per-journal search cards
SessionJournalSearchCardIndex Caches one search card per journal in search-card.json, rebuilt when the journal changes
SessionJournalSemanticIndex Optional semantic search index in the journals folder (.semantic-index/), built with the local embedding model

UI Module Organization

The UI layer is built on JavaFX and organized into logical components:

Component Purpose
MainWindow Top-level application window containing menu bar, tab bar, terminal panes, dashboard, SFTP browser
TerminalTab One terminal tab with split-pane support (in TerminalView) and inline AI activity panel
ConnectionEditDialog Multi-tab editor for connection details (SSH, tunnels, jump server, logging, etc.)
SFTPManagerTab Dual-panel file manager tab for local and remote file operations
SnippetEditDialog Snippet editor with a Monaco code editor, syntax highlighting, AI assistance, and Mermaid flowcharts
LocalModelManagerPane Searches/downloads/imports GGUF files and controls concurrent llama.cpp sidecars
RagKnowledgeStorePane Creates knowledge stores, previews sources, displays persisted indexing state, synchronizes them, and runs retrieval tests
JobSchedulerDialog Job creation, scheduling, and journal inspection
QuickConnectDialog Fast connection search and frequently-used connection shortcuts

Security Architecture

Master Password and Encryption

  1. First Launch: User creates a master password (minimum 6 characters, strength-checked via zxcvbn)
  2. Storage: Password is hashed using PBKDF2 with 310,000 iterations and stored in ~/.kortty/master.key
  3. Unlocking: Master password unlocks all encrypted data (connection passwords, SSH key passphrases, credentials, backup archive passwords)
  4. Encryption: AES-256-GCM with random IVs; encryption/decryption happens on-demand when accessing sensitive fields

Host Key Verification

  • Interactive Terminal/SFTP/Mosh bootstrap: One shared TOFU verifier is keyed by normalized host name and port. First use shows the OpenSSH SHA-256 fingerprint with No as the default; an exact match is silent, while a changed key is hard-blocked without retry. Only a connection the user opened in a terminal tab or the SFTP manager may replace a changed key, after an explicit fingerprint confirmation and as a compare-and-swap against the reviewed key; background callers build their verifier with the never-replace policy.
  • Interactive storage: ssh-host-keys.properties stores public-key material through an atomic replacement guarded by both in-process and cross-process locks. Its transient .lock companion is not backed up.
  • JobScheduler: Unattended SSH, SFTP, and Rsync use separate connection-ID-based pins in job-scheduler.xml, including OpenSSH public-key material needed by Rsync. A per-job override can disable that verification only when the risk is explicitly accepted.

Credential Management

  • Environment-specific: Credentials can be scoped to Production, Development, Test, or Staging
  • Glob patterns: Server patterns like *.example.com or 10.0.0.* automatically match connections
  • Stored encrypted: All credential passwords use AES-256-GCM

Embedded AI and RAG isolation

  • Each embedded model process binds only to 127.0.0.1 on a random port and requires a generated API key stored in an owner-only temporary file.
  • The fixed llama.cpp launch command enables offline mode and disables the web UI, agent, UI MCP proxy, and slots endpoint; inherited LLAMA_ARG_* and Hugging Face token variables are removed.
  • Hugging Face tokens and AI profile API keys are encrypted with the master password. Model downloads are pinned to an immutable revision and verified with SHA-256.
  • RAG accepts only centrally allowlisted, content-validated text. Retrieved excerpts are bounded and wrapped as explicitly untrusted data so indexed instructions cannot replace korTTY's system/action contract.
  • Runtime update indexes are verified as exact bytes with Ed25519 before package URLs are parsed; packages also have signed size/SHA-256 metadata and safe ZIP extraction limits.
  • Runtime withdrawals are persisted before process shutdown through a runtime-root denylist plus package marker. Active pointers and unsafe rollback-history entries are removed, registered models are rebound to a non-executable quarantine marker, and the launch path rechecks the marker so stale registry state cannot revive a withdrawn binary.
  • A package that passes the lightweight version health check remains pending until its first real GGUF-backed authenticated API start. LlamaRuntimeFirstLaunchRecovery confirms it on readiness or restores the newest healthy non-revoked installation and model bindings after a start failure.
  • The independent model/prompt catalog has its own Ed25519 trust root and strict schema. Its cache is reverified before use; a missing key disables both cache trust and network refresh and selects the compiled bootstrap.
  • Remote Qdrant uses HTTPS, with plain HTTP restricted to a loopback test/local service.

Plugin System

KorTTY supports terminal effect plugins via Java ServiceLoader for customizing terminal appearance and behavior.

Plugin Architecture

SPI Interface Responsibility
TerminalEffectPlugin Plugin metadata (ID, name, description) and session factory
TerminalEffectSession Lifecycle hooks (init, render, cleanup) and optional TtyConnector wrapping
TerminalEffectContext Access to overlay API, current SithTermFX widgets, animation speed, appearance
TerminalEffectAppearance Optional font/color/cursor overrides
TerminalEffectConnectorWrapper Transparent command stream wrapper; KorTTY can safely unwrap

Plugin Loading

  • Bundled plugins: Loaded from the application classpath (e.g., MOTHER effect)
  • External plugins: Imported as .jar files into ~/.kortty/plugins/
  • Management: Enable/disable individual plugins via Plugins → Terminal Effects without uninstalling
  • Exportable plugins: Some plugins can be exported as standalone JARs for distribution

Security Note

External plugins are trusted local code and are not sandboxed. Only import JARs from sources you trust. Dependencies not already bundled with korTTY must be shaded into the plugin JAR.

External Dependencies

KorTTY relies on carefully curated, production-tested dependencies:

Category Library Version Purpose
SSH Apache SSHD (core, common, sftp) 2.19.0 SSH protocol implementation
BouncyCastle (bcprov, bcpkix) 1.86 Cryptographic provider, SSH key parsing and Ed25519/EdDSA key support
Terminal SithTermFX (core, ui) 1.2.7 Terminal emulator engine
Lanterna 3.1.5 Text-based UI components
pty4j (JetBrains) 0.12.25 PTY allocation for Mosh
jSerialComm 2.11.4 Serial-port connections
Platform JNA (jna, jna-platform) 5.19.1 Native desktop power-management integration
Data Jakarta XML Bind 4.0.5 (jaxb-runtime 4.0.9) JAXB serialization
Gson 2.14.0 JSON parsing
zip4j 2.11.6 ZIP encryption
jtokkit 1.1.0 Token counting for AI requests
PDFBox 3.0.8 PDF export and RAG text extraction
Archive Apache Commons Compress 1.28.0 TAR, BZ2, XZ support
Tukaani xz 1.12 XZ compression
zstd-jni 1.5.7-16 zstd compression for rotated session-journal parts
UI JavaFX 21.0.12 Application framework
AtlantaFX Base 2.1.0 Seven selectable Primer, Nord, Cupertino and Dracula JavaFX user-agent themes; its transitive OpenJFX dependency is excluded
Monaco Editor 0.56.0 Code editor component
Mermaid 12.0.0 Local diagram parsing, SVG rendering, and PNG rasterization
MathJax 3.2.2 Local AI-chat formula rendering
google-java-format 1.36.1 Java code formatting
Utilities jfiglet 0.0.9 ASCII art banners
zxcvbn 1.9.0 Password strength (offline)
Logging SLF4J / Logback 2.0.19 / 1.6.3 Structured logging
Optional mosh4j 2.0.3 Mosh protocol (dynamically loaded)
Local AI llama.cpp llama-server Source-pinned runtime package Local GGUF chat-completions and embeddings sidecar

Dynamic Dependencies

  • mosh4j: Its five SHA-256-pinned, architecture-specific release JARs and protobuf are bundled in native builds and dynamically loaded only when Mosh is needed. The child loader reuses Bouncy Castle from the application parent instead of shipping a second copy.
  • rsync / ssh: External commands used by JobScheduler Rsync jobs
  • ffmpeg: Optional; used for terminal recording video export
  • llama.cpp runtime: Downloaded independently under ~/.kortty/llm/runtime/; CPU packages cover every supported platform, Metal is available on macOS, and Vulkan is available for supported Windows/Linux targets. It is never folded into the base installer.
  • Qdrant: Optional external vector service. Remote endpoints require HTTPS; HTTP is loopback-only. Local HNSW is dependency-free and remains the default knowledge-store backend.

Build Process

Compilation and Packaging

  1. SithTermFX build: Cloned and built locally (Maven); its accidentally published JUnit dependency is excluded from the runtime.
  2. Browser assets: An isolated, pinned Node.js is used only at build time for Monaco and for JavaFX-WebKit compatibility processing of the SHA-256-pinned Mermaid bundle. Monaco's editor and diff pages share one mode-aware IIFE/CSS pair while retaining all five workers and language services. Mermaid and MathJax remain separate lazy resources; Prettier Standalone with five selected plugins and the sql-formatter UMD build run without Node at runtime.
  3. External formatter payload: Only shfmt, Perl::Tidy and their manifest are staged beside the app; the logo video is stored once per source surface as H.264/yuv420p at 640×360 without audio.
  4. Clean native staging: prepareJpackage uses a final Gradle Sync, so obsolete dependencies, formatter trees and Mosh architectures are deleted. Bouncy Castle is deduplicated, and JNA, pty4j, zstd-jni and jSerialComm are repacked with only the current target's native paths and binary architecture.
  5. Native packaging and gates: The selected Gradle JDK 25 toolchain supplies jpackage for .app/.dmg, .exe/.msi, .deb and .rpm output. scripts/package-size-report.py emits JSON/Markdown component reports and CI enforces the committed release comparison, at least 10% installer reduction, absolute app/DMG limits and frozen verified-size budgets with 2% tolerance.
  6. JavaFX dependency alignment: verifyJavaFxDependencyAlignment checks every resolved compile, runtime and test classpath before compilation and packaging. Standard OpenJFX modules must remain at 21.0.12; only the compile-time jdk-jsobject bridge uses its separately pinned version, and it may not enter a runtime classpath.
  7. llama.cpp runtime packaging: Separate Gradle tasks verify the pinned upstream tag/commit/archive SHA-256, build only llama-server plus required shared libraries, stage a backend-specific tree, and produce a reproducible immutable ZIP and signed-index descriptor input. The weekly runtime workflow opens candidate PRs; a scope job runs the full platform/backend matrix only when the pin file, the workflow, or the llama Java sources changed (a build.gradle.kts-only change gets a single smoke leg); every built leg runs a native link smoke, the reference package runs the full authenticated chat/embedding/JSON/sleep/parallel-sidecar contract, and a protected llama-runtime-signing environment with required reviewers gates manual stable promotion from main.
  8. Model/prompt catalog promotion: A separate manual main-only workflow validates the canonical strict-schema JSON, requires a sequence greater than the latest immutable release, runs schema/trust-chain tests, matches the signing key to the application trust root, signs the exact bytes, and publishes through the reviewer-protected ai-catalog-signing environment without a preview channel.

Classpath and Module Path

  • Compile-time: On JDK 25 the JavaFX jdk-jsobject artifact is supplied on the upgrade module path so WebView bridges compile consistently; the module is absent from JDK 26.
  • Packaging-time: Gradle resolves and invokes the same Temurin JDK 25 toolchain used for compilation, so the trimmed runtime contains jdk.jsobject even when the system java/jpackage is newer.
  • Module path: JavaFX, SithTermFX and Apache SSHD are available during compilation; unused javafx.fxml and java.scripting are not included in the packaged runtime.

Data Flow and Integration Points

SSH Connection Flow

User Input (Quick Connect)
    ↓
Connection Manager (lookup saved or new connection)
    ↓
Worker-thread SSH handshake + progress UI
    ↓
Shared host:port TOFU verification (Terminal/SFTP/Mosh bootstrap)
    ↓
SshTtyConnector (Apache SSHD) or Mosh connector
    ↓
Terminal Pane (SithTermFX rendering)
    ↓
Dashboard (status display, job monitor)

AI Integration Flow

Selected Terminal Text
    ↓
AI Action + deterministic Text/Coding role
    ↓
AI profile (HTTP / CLI / embedded GGUF)
    ↓
Action contract → AI Skills → optional bounded RAG → model preset
    ↓
AiServiceFactory (remote provider, local CLI, or runtime lease)
    ↓
AI Response Tab (render with follow-up composer)
    ↓
Persistence (saved chats stored in global-settings.xml)

Job Scheduling Flow

User creates scheduled job
    ↓
JobSchedulerService stores in job-scheduler.xml
    ↓
JobSchedulerJobRunner runs on schedule (background thread)
    ↓
JobJournalEntry records output with redaction
    ↓
Menu-bar status displays next runs / live countdown

Logging and Diagnostics

  • Log file: ~/.kortty/logs/kortty.log, rolled over daily, with the log directory and retention set under Settings → Logging (SLF4J with Logback backend)
  • JMX monitoring: MBean de.kortty:type=SSHClient exposes the live SSH and Mosh terminal connections (each split pane counts, local shells do not), memory and uptime; BufferedTextSize is not tracked and stays 0
  • JobScheduler journal: Detailed execution logs with configurable retention (14 days default, unlimited if set to 0)
  • Project screen snapshots: The last visible screen of each terminal tab of a saved project, gzip-compressed in ~/.kortty/history/ and shown locally (never sent to the server) when the project is reopened with Auto-Reconnect
  • Terminal recording: Optional replay files in ~/.kortty/recordings/
  • Test isolation: The test Logback configuration writes to its own console only and never creates or appends to the real user's ~/.kortty/logs

Thread Model

  • UI Thread: JavaFX application thread handles all rendering and user interaction
  • SSH Session Threads: One thread per active SSH connection (Apache SSHD pool)
  • Split-connection handshake: Same-server and newly selected split connections perform network setup on a worker while a JavaFX progress dialog keeps host-key and keyboard-interactive prompts responsive.
  • Job Executor Threads: Background thread pool for JobScheduler execution
  • AI Chat Threads: Background threads for API requests (non-blocking UI)
  • AI catalog refresh: The first catalog consumer gets verified cache/bootstrap data immediately and starts at most one background stable-channel refresh.
  • Local-model provisioning: Setup-assistant metadata inspection, signed runtime installation, resumable GGUF download/verification, registration, and real chat/embedding tests run on background futures; JavaFX receives only progress and final state updates. Text/Coding/RAG assignments are persisted after every selected model passes.
  • llama.cpp sidecars: One native process per compatible loaded GGUF configuration; different models can load and generate concurrently, while leases keep active requests from being stopped. Runtime-configuration saves first request an idle stop and are rejected while a lease is busy.
  • RAG workers: Source previews, extraction, embedding batches, and HNSW candidate builds run outside the JavaFX thread; a daemon WatchService thread debounces automatic source changes. The pane permits only one active scan/index operation and returns the reviewed preview to JavaFX before configuration or indexing changes.
  • RAG coordinator: One serialized worker and watcher per store prevents overlapping writes, reconciles Automatic sources during application startup independently of credential/JobScheduler startup, persists source hashes/counts/status, and reloads UI state after completion.
  • File I/O Threads: Async writes for history, journal, and recordings
  • Web formatter requests: Background callers are serialized with one total timeout; creation, loading and invocation of the lazy Prettier/SQL WebView remain confined to the JavaFX application thread, and failures discard the engine generation.
  • Mermaid render requests: Background callers receive CompletableFuture results while all access to the single lazy renderer WebView stays on the JavaFX application thread. Requests are serialized; cancellation, a 30-second timeout, or a WebEngine error discards the engine generation, and idle cleanup releases the hidden WebView.

Extensibility Points

For Plugin Developers

  1. Terminal Effects: Implement TerminalEffectPlugin and register via ServiceLoader (see TERMINAL_EFFECT_PLUGINS.adoc)
  2. Custom Formatters: Add support in the snippet editor for new languages
  3. AI Skills: Import custom AI instruction sets via AI → AI Manager → AI Skills

For Integrators

  1. JAXB Repositories: Extend XMLConnectionRepository or add a store beside CredentialManager for custom data sources
  2. JobScheduler Actions: Add new action types to JobSchedulerJobRunner (e.g., custom SFTP operations)

Performance Considerations

  • Lazy loading: Connection details loaded on demand, not all at once
  • Compression: Project screen snapshots and terminal logs use gzip; rotated session-journal parts use zstd (legacy .gz parts stay readable)
  • Throttling: Terminal rendering updates are batched to reduce UI thread load
  • Memory pooling: Large buffers for terminal text and SFTP file listing are reused

Security Best Practices

  1. Master password: Set a strong, unique password; it is never transmitted or logged
  2. SSH keys: Store in ~/.kortty/ssh-keys/ for backup inclusion; passphrases are encrypted in ssh-keys.xml, and the AES-256/GPG-encrypted backup carries the key files
  3. Host key verification: Verify the OpenSSH SHA-256 fingerprint before accepting an interactive first-use prompt; keep host-key pinning enabled for unattended JobScheduler execution
  4. Backup encryption: Use password-protected ZIP or GPG encryption
  5. AI profiles: Prefer an integrated local GGUF model for sensitive data; verify the trust and data policy of every remote endpoint
  6. Avoid logging secrets: JobScheduler journal redacts stored secrets before persistence