A cross-platform terminal emulator implemented in C# on .NET 10, published with NativeAOT, and rendered with Avalonia 12 / Skia:
- ConPTY on Windows and a real forkpty transport on Linux and macOS
- Azure Cloud Shell for remote Azure sessions
- selectable built-in or Ghostty VT engine
- Windows Terminal-compatible
settings.json, actions, keybindings, anddtCLI
This is the Devolutions Terminal
source tree. Projects, namespaces, and the GUI host use Devolutions.Terminal.*.
The CLI executable is dt.
dotnet test Devolutions.Terminal.slnx
dotnet run --project src/Devolutions.TerminalOn Windows, Shift-click a profile in the new-tab dropdown to open it in an
elevated tab without elevating the Terminal window. Elevated tabs show a shield,
including in compact view; Show admin shield in Settings controls the badge.
Install gsudo and ensure gsudo.exe is on
PATH before starting Terminal. To always elevate a profile, enable
Run this profile as Administrator in its settings (elevate: true). The
newTab/splitPane action's elevate argument can override that default.
Elevation is only available for local Windows sessions; UAC approval is still
required, and cancelling it does not launch an unelevated shell.
dotnet publish src/Devolutions.Terminal -c Release -r win-x64 --self-containedThe native executable is written to
src/Devolutions.Terminal/bin/Release/net10.0/win-x64/publish/Devolutions.Terminal.exe.
macOS NativeAOT app bundles (Darwin only):
pwsh scripts/Build-MacOsPackage.ps1 osx-arm64 2026.3.0 artifacts/packages
pwsh scripts/Test-MacOsPackage.ps1 osx-arm64 artifacts/packages/*.zipLinux x64 and ARM64 NativeAOT packages are built on Linux with:
scripts/Build-LinuxPackage.sh linux-x64 2026.3.0 artifacts/packages all
scripts/Build-LinuxPackage.sh linux-arm64 2026.3.0 artifacts/packages all
bash scripts/Test-LinuxPackage.sh linux-x64 artifacts/packages/*-linux-x64.*The builder emits .tar.gz, .deb, .rpm, .AppImage, and .sha256 files.
Every format consumes the same normalized /opt/devolutions-terminal payload
and /usr desktop integration layout. SOURCE_DATE_EPOCH controls package
timestamps.
After extracting a tarball at the filesystem root:
sudo /opt/devolutions-terminal/linux/Install-LinuxDesktopIntegration.sh install
/opt/devolutions-terminal/linux/Install-LinuxDesktopIntegration.sh register-protocol
/opt/devolutions-terminal/linux/Install-LinuxDesktopIntegration.sh set-default-terminal.\src\Devolutions.Terminal.Package\Scripts\Build-Packages.ps1Development signing, trust, install, validation, and uninstall commands are in
src/Devolutions.Terminal.Package/README.md.
Settings are stored at %LOCALAPPDATA%\Devolutions\Terminal\settings.json on
Windows, under $XDG_CONFIG_HOME/devolutions-terminal on Linux (usual
~/.config fallback), and at
~/Library/Application Support/Devolutions/Terminal/ on macOS.
Set WT_BASE_SETTINGS_PATH to use a directory for settings.json and
state.json (same contract as Devolutions' Windows Terminal distribution).
Set DTERM_SETTINGS_PATH (or WT_DOTNET_SETTINGS_PATH) to load a specific
settings file. On Windows, WT_PARENT_WINDOW_HANDLE embeds the window as a
child of that HWND. alwaysShowTabs: false hides the tab row when only one
tab is open.
dt --isolated (or Devolutions.Terminal.exe --isolated) runs the launch in
its own process: it neither forwards to a running instance nor becomes the
broker for later launches, so every window keeps its own environment,
WT_PARENT_WINDOW_HANDLE, and settings directory. -w targeting of other
windows is unavailable, and persisted layouts are neither restored nor saved.
It is the per-launch equivalent of Windows Terminal's
compatibility.isolatedMode setting, which Windows Terminal 1.23 removed.
Set "experimental.terminalEngine": "ghostty" to use the pinned
libghostty-vt engine globally. A profile can override it with "builtin" or
"ghostty". ConPTY remains the Windows process transport for both engines.
The compiled-XAML settings editor is documented in docs/settings-editor.md.
Devolutions.Terminal.Core VT parser + text buffer + terminal engine
Devolutions.Terminal.Ghostty NativeAOT-safe libghostty-vt engine adapter
Devolutions.Terminal.Render Immutable plans + HarfBuzz/Skia glyph renderer
Devolutions.Terminal.Connection ConPTY + Linux PTY + Azure Cloud Shell + browser shell
Devolutions.Terminal.Settings Layered Windows Terminal-compatible JSON settings
Devolutions.Terminal.Control Avalonia TermControl renderer
Devolutions.Terminal.App Tabs, title bar, panes, actions, window behavior
Devolutions.Terminal NativeAOT executable and composition root
Devolutions.Terminal.Browser Avalonia WebAssembly host (in-process dt-wasm shell)
Devolutions.Terminal.Browser.Host Loopback server for profile tabs and a real PTY
The measured remaining parity contract is in docs/parity-status.md. Architecture decisions are recorded in docs/decisions. Renderer contracts are documented in docs/renderer.md. Control, clipboard, IME, and accessibility contracts are documented in docs/control-accessibility.md. Advanced VT protocols are documented in docs/advanced-vt-protocols.md. Azure Cloud Shell is documented in docs/azure-cloud-shell.md. The browser WASM host is documented in docs/browser.md. Build and release gates are documented in docs/release.md.
Devolutions.Terminal.Control is also published to
nuget.org as a
reusable, self-contained Avalonia terminal control package (it bundles
Core/Render/Connection/Settings internally) for embedding in other
Avalonia applications. See the "NuGet package" section of
docs/release.md for packaging and consumption details.
Recording actions operate on the active terminal pane. startRecording captures
future PTY output, stopRecording retains the completed recording,
saveRecording opens a native Save As dialog and exports an asciicast .cast
file, and replayRecording replays the active pane's latest capture in a
separate read-only tab with its original timing. The recorder writes v2 by
default and the core API can also export v3 with
recording.ToJson(AsciicastFormat.V3). openRecording detects and replays both
v2 and v3 files, even when the current pane has no recording. Pickers start in
the recordings directory beside settings.json.
All path-taking actions also accept an explicit path for keybindings,
command-line activation, and automation. A path on startRecording enables an
auto-save workflow: stopping the recording writes directly to that path.
Exported v2 files are compatible with v2 players such as asciinema play;
imported v3 files retain their relative event timing during replay.
To stream the active tab, open the command palette and choose Start
streaming..., paste the producer ws:// or wss:// URL, and choose
Asciinema v3 (recommended), Asciinema v2, or DVLS v2. The recording
bar changes to Live while connected. Choose Stop streaming from the
palette (or use the bar's Stop button) to flush and close the stream. DT
remembers the URL only for the current process because producer URLs may
contain access tokens.
NuGet consumers can also stream an active recording to an asciicast-compatible WebSocket push endpoint:
await terminal.StartRecordingAsync(
new Uri("wss://gateway.example/jet/jrec/push/session?token=signed-token"));
// Use the terminal normally.
await terminal.StopRecordingAsync();The control preserves authentication query parameters, adds
fileType=asciicast, and sends the visible starting screen plus subsequent
output as asciicast v2 JSONL text messages. Endpoint discovery and token
acquisition stay in the embedding application.
Official asciinema servers are also supported. Create a live stream through the
server API, then pass its ws_producer_url and select v2 or v3:
await terminal.StartRecordingAsync(
new Uri(streamResponse.WsProducerUrl),
AsciicastFormat.V3);This negotiates the matching v2.asciicast or v3.asciicast WebSocket
subprotocol. V3 output uses relative event deltas; the legacy URI-only overload
remains compatible with DVLS push URLs and server-side first-message detection.
While a recording is active, its tab shows a red recording indicator and the active pane displays elapsed time, format, destination, and Stop/Save controls. Stopped captures remain available from the same bar for saving or replay. A yellow tab indicator marks an unsaved capture, and closing its pane, tab, or window asks for confirmation even when normal close confirmations are disabled.
Replay tabs include pause/resume, restart, progress, and 0.5x-2x playback speed controls. Replay remains read-only and can be closed without affecting the original terminal session or recording.
The actions can be invoked from the command palette or assigned in
settings.json; for example:
{
"command": {
"action": "startRecording",
"path": "%USERPROFILE%\\Desktop\\demo.cast",
"format": "v3"
},
"keys": "ctrl+shift+r"
}Large or multi-line pastes requiring confirmation are cancelled unless explicitly approved. The application prompts asynchronously; closing the prompt cancels the paste. Embedded controls without a confirmation handler also cancel warned pastes.
warning.confirmOnClose applies to user-initiated window, tab, pane, and bulk
close actions. never skips confirmation; always confirms closing any running
session; automatic confirms when an action closes more than one running session
(the legacy confirmCloseAllTabs behavior). Already-exited sessions and automatic
process-exit cleanup never prompt.
PTY input is queued in order off the UI thread, with limits of 256 pending writes and 4 MiB (including framing on Unix). Overflow rejects the entire new write and reports an error rather than blocking or silently dropping input. Async writes complete after transport delivery; caller cancellation skips writes not yet started. Cancelling an in-flight write terminates its session because input may have been partially delivered and Unix framing cannot safely resume. Closing a blocked Unix session has a one-second grace period before host termination; undelivered input is reported.
The editor disables options that are currently retained only for settings-file
compatibility: compatibility.textMeasurement, compatibility.ambiguousWidth,
and disableAnimations. The terminal engine determines text measurement and
character widths. experimental.detectURLs detects http, https, ftp, and www
URLs in terminal text; explicit OSC 8 hyperlinks remain supported.
Window/pane animation effects are not configurable through disableAnimations.
Broker retries share active requests and retain completed responses for at least five seconds after completion. Admission is bounded at 128 active requests and 1024 total retained requests. When full, new requests receive an explicit unavailable response without executing their action; existing retries still join their original operation.
The port tracks Windows Terminal settings, actions, VT dispatch, command line,
and settings-page surfaces in
compat/windows-terminal.json. Tests use that
checked-in snapshot. Regenerating it requires a separate Microsoft Windows
Terminal C++ checkout:
dotnet run --project tools/Devolutions.Terminal.PortInventory -- <windows-terminal-checkout> compat/windows-terminal.json