Safe in both directions

Safe to paste. Safe to read.

Pasted commands are sanitized and program output is neutralized, so neither what you copy in nor what a program prints out can hide invisible characters, bidi overrides or escape sequences. Other modes trade some of that back on purpose: see all four side by side.

A GNOME Terminal window whose title bar has been silently changed by the file it is displaying to read 'gnome-terminal (window title injected)'. The text is a pre-deploy verification checklist for acme-cli release v3.2.0 that reads as entirely trustworthy: it fetches from https://example.com, reports a good maintainer signature, an attachment named q3-report.exe.pdf 'reported document-only, safe', a deploy under the account administrator@corp.internal, and a support contact ops@example.com, ending 'All preflight checks passed -- ready to deploy'. Only the injected title hints anything is off.
The same file shown by secure-terminal in Show mode, its own title bar unchanged. It flags every trap in the same bytes: both 'example.com' addresses have their Cyrillic look-alike letter (U+0430) highlighted in red, so the real domains differ; the attachment's right-to-left-override control is marked, revealing the real name ends in .exe not .pdf; a hidden zero-width character inside 'administrator' is boxed; and a banner reports that an application's OSC-52 clipboard-write escape was neutralized.
The same file, cat'd in two terminals with the same font. Left, secure-terminal in Show mode; right, a traditional terminal (GNOME Terminal). It is a routine "verify this release before you deploy" checklist -- and every reassuring line hides a still-applicable trick, spread left to right: a Cyrillic look-alike in two example.com addresses (U+0430), a right-to-left override that shows an .exe as a .pdf, a zero-width character inside "administrator", a silently hijacked window title, and a silent OSC-52 clipboard overwrite. The traditional terminal renders all of it as a trustworthy session; secure-terminal shows the same bytes and flags every one. Real screenshots, captured under a labwc compositor; title-hijack behaviour varies by terminal (konsole, for instance, resets it). Drag to compare, or click a side to open it full size.

What is this? A terminal whose display shows printable ASCII in its default display mode (everything else is neutralized to a visible, inert placeholder) and which runs no escape-sequence parser on program output beyond safe colour and four edits clamped to the line being written. Paste a command you copied from the web, or read text a program printed, and trust that what you see is what is there. When you need more, an opt-in TUI mode runs full-screen programs, ssh, vim, htop, through a restricted screen model that still filters every character.

Safe by design

The security property is not a filter bolted on. It is what the terminal can and cannot do by default.

Output is printable ASCII by default

Program output is rendered as printable ASCII plus newline and tab. Bidi overrides, zero-width characters, homoglyphs, ANSI escapes and control bytes never reach the screen as anything but their neutralized form. A hostile filename in ls, a forged [ OK ] in a log, a Trojan-Source comment: none of them can reorder what you read or repaint a line you have already scrolled past. One honest caveat: a program can still redraw the line it is currently writing -- see what a program can still do to its own line.

No escape parser in CLI mode

By default there is no general ANSI parser to attack: no vertical or absolute cursor movement, no alternate screens, no OSC hyperlinks, no window-title control, no clipboard-write escape (OSC 52). Parsing attacker-influenced escapes is the entire vulnerability class; a terminal that will not parse them cannot fall for them. Two narrow exceptions are honoured on purpose - safe, contrast-guarded colour, and the four line-local edits a shell's line editor emits, which cannot leave the line being written. Each OSC feature is a separate, off-by-default toggle you may enable at your own risk, and turning one on dims a security indicator so the extra surface is never invisible.

What a program can still do to its own line

One honest caveat, stated narrowly. CLI mode honours the four line-local edits a shell's line editor emits (cursor forward/back, cursor-to-column, erase-in-line), all clamped to the line currently being written - so a program can overwrite what it itself just printed on that line, exactly as a \r progress bar does, but nothing can reach a line you have already scrolled past. Transcript integrity is per-line: everything above is append-only, and the overwritten text is gone (capture it with stcat if you need a byte-exact record). You can drop even this with View -> Line editing (line_edits, default on). Full detail on the compatibility page.

Line-oriented input, and you can never get stuck

You type printable ASCII and the usual control keys (Ctrl+C, Ctrl+A, Ctrl+R and the rest). They send the control byte exactly as a real terminal does, so a shell in its normal line mode interrupts on Ctrl+C while a raw-mode program - an editor, or a CLI that shows "press Ctrl+C again to exit" - reads the byte itself. A signal key is echoed in caret notation (^C, ^\) so you always see the interrupt you sent, even when the shell would print nothing. A Terminate button (SIGTERM then SIGKILL) is the guaranteed escape hatch for a program that ignores everything, so you can never get stuck.

Memory-safe

The code that reads hostile bytes is written in a memory-safe language (Python). The traditional terminal emulators, written in C, have a long history of memory-corruption bugs in exactly this parsing path (escape and image-decoder overruns); here that entire class, buffer overflows and use-after-free driven by attacker-controlled output, cannot happen by design.

Safe, and actually usable

Safety you can live with day to day, not a museum piece.

Paste warning

Paste anything with unicode or control characters and a review bar opens inside the window, holding the paste before a byte reaches the shell. It shows the paste four ways - as it looks, each hidden character named inline as a <U+XXXX> badge, and exactly what each send button would deliver (stripped to ASCII, or with printable unicode kept) - all risk-coloured by the terminal's own pipeline. Input is suspended and both send buttons are countdown-gated while it is held, so a stray key reaches nothing. When to warn is configurable (always / only on unicode-or-control, the default / never); never turns off the prompt, not the safety - a paste is always sanitised to ASCII, there is no pass-through mode. A multi-line paste is held too, so a hidden second command cannot run the instant you paste.

Copy is reviewed the same way, in the other direction - the same bar guards text leaving via copy, configured separately because copy and paste are opposite trust directions. The two screenshots below show both bars.

Four unicode display modes

Per tab: Box (every non-ASCII character becomes an inert box, coloured by risk class), Reveal (each as a <U+XXXX> badge, to inspect exactly what is there), Detail (the same badge with the codepoint's Unicode name inline, the safe default), and Show (render legitimate unicode so you can read a log, each glyph still tinted by risk class (except inert box-drawing and block elements, drawn in the program's own colour) - a homoglyph confusable with ASCII wears a louder colour than honest foreign text, and a non-ASCII space is shown as a distinct marker and copies as _, never as a space). The figure below is one ls listing in all four.

Show does not mean run. All four modes remove escape sequences first (there is no general escape parser in CLI mode), so nothing a program prints - a title change, a clipboard write, an alternate screen, a jump to another line - is ever interpreted, in any mode, Show included. The mode only changes how the remaining, already-safe characters are drawn, never whether escapes are honoured; the only escapes honoured in every mode are safe colour and the four line-local edits described under the current line.

The same ls listing, all four modes - three of the filenames are lying (a right-to-left override, a Cyrillic look-alike, a zero-width space) and two are honest non-ASCII (résumé.pdf, a CJK name).
One ls listing rendered four times by secure-terminal, ending with Detail, the default. Box: every non-ASCII byte, hostile or honest, becomes a single coloured placeholder box, so the accented and CJK names are boxed too. Show: the honest resume.pdf and the CJK name are readable as themselves, while the Cyrillic look-alike 'a' is kept but tinted red and the bidi override and zero-width space still collapse to placeholders. Reveal: each hidden character is named inline as a badge, U+202E, U+0430, U+200B, U+00E9, U+4E2D, U+6587. Detail: the same badges expanded to full Unicode names, RIGHT-TO-LEFT OVERRIDE, CYRILLIC SMALL LETTER A, ZERO WIDTH SPACE, LATIN SMALL LETTER E WITH ACUTE, CJK UNIFIED IDEOGRAPH.
This is also the honest answer to "is it ASCII only?" - it is not, and the panels show why. Box is ASCII in text but draws a non-ASCII placeholder glyph, and it boxes honest non-ASCII too. Show deliberately keeps real printable non-ASCII, tinting a look-alike rather than removing it. Reveal and Detail are pure ASCII, naming what was hidden. What holds in every mode is narrower and more useful: no invisible, bidi or control character reaches you unmarked.

Transcripts safe to open anywhere

Save a tab's scrollback to a file. The saved file is lossless - each neutralized character is named inline (<U+0430 CYRILLIC SMALL LETTER A>), never collapsed to a bare _ - and pure ASCII unless you opted into Show mode's real glyphs. So it is safe to open in any editor, unlike a normal terminal's raw log which can carry the very escapes this terminal refuses, and it still records exactly what was there.

Guaranteed exit

Signal keys reach the foreground program even in raw mode, and the Terminate action force-kills a program that ignores everything. You are never stuck inside something you cannot get out of.

Granular escape-feature controls

The OSC escapes that reach out of the grid - window title, desktop notification, clickable hyperlink, clipboard write, clipboard read, palette change, working-directory report - are each a separate, off-by-default toggle with a one-line note on what it exposes. (The iTerm2 OSC 1337 extensions have no toggle - they can never be safely enabled, so they are always neutralised.) A three-part security indicator dims from green toward red as you enable riskier features. When a program tries to use a feature you left off, a dismissible banner tells you once per feature per tab - visible but never nagging, and silenceable globally or per feature.

The paste review - a curl | bash line hiding two Cyrillic homoglyphs (in example and bash), a zero-width space and a right-to-left override.
secure-terminal's in-window paste-review bar showing the hostile paste four ways: the Original pane, where the bidi override reorders the trailing comment; the Detail pane, naming each hidden character inline in its risk-class colour as U+0430 CYRILLIC SMALL LETTER A, U+200B ZERO WIDTH SPACE and U+202E RIGHT-TO-LEFT OVERRIDE; the Paste-stripped pane, where the homoglyph domain collapses to exmple.com and 'bash' to 'bsh'; and the Paste-with-unicode pane, which keeps the look-alike example.com (its Cyrillic a tinted) but drops the invisibles. Both send buttons are countdown-disabled; only Reject is available immediately.
The four panes read left to right: the Original as it arrives, Detail naming every hidden character inline (<U+0430 CYRILLIC SMALL LETTER A>), and the two send previews - Stripped drops the look-alikes so the domain changes to exmple.com, while keeping unicode preserves the deceptive example.com but still removes the bidi and zero-width. Rendered headless from the real bar.
The same bar, for copy going the other way - selecting and copying that same line in Show mode, before it reaches the system clipboard.
secure-terminal's review bar for a copy: the summary reads 'This copy would carry 1 bidirectional control, 1 invisible character, 2 non-ASCII characters onto the clipboard'; the four panes are Original, Detail (naming each hidden character in its risk-class colour), 'Copy stripped puts' (the homoglyph domain collapsed to exmple.com), and 'Copy with unicode puts' (the look-alike example.com kept, its Cyrillic a tinted). The buttons read Don't copy, Copy stripped, Copy with unicode, with no countdown since a copy is not executed.
Copy is reviewed with the same bar, just relabelled for the other direction (Don't copy / Copy stripped / Copy with unicode) and configured by its own setting. There is no countdown - a copy is not executed, so the anti-fat-finger gate the paste needs does not apply.

TUI mode: run full-screen programs

When you need to run ssh, an editor like vim, or htop, opt in with your eyes open.

Opt-in, clearly flagged / ssh, vim, htop, tmux

The strict default cannot run curses apps. TUI mode (per tab, off by default, marked with a yellow risk indicator) interprets escapes inside an isolated in-memory screen model (pyte) that has no OS reach: by default it cannot set the window title or touch the clipboard. Each such OSC side-effect is a separate opt-in toggle (off by default) that dims a security indicator when enabled, so those spoofing vectors stay closed unless you deliberately open one. Every character it places is still ASCII/unicode-filtered, and colours use a contrast guard so nothing can be painted invisibly.

What you give up: a program can draw a misleading interface within its own screen, so only run programs you trust. This is "restricted-emulator safe," not "safe by design." The default CLI mode, and everything the guarantee rests on, is unchanged.

# in TUI mode, verified end to end: ssh, vim, htop, tmux and apt's progress bar all render correctly, with every cell still filtered and the title/clipboard still off-limits.

Everyday comforts

The conveniences a terminal is expected to have, without loosening the model.

Tabs

Named, colour-coded, each its own shell. Per-tab theme, zoom, unicode mode and colours. Rename by double-click; a user name and a program title are kept separately.

Safe optional colours

Full ANSI colour -- 16-colour, 256-colour and 24-bit truecolor -- off by default, with a contrast guard so text can never be invisible or hide itself against the background (a crafted red-on-red is forced legible). Honours NO_COLOR and TERM=dumb.

Session restore

Tabs and their scrollback survive a restart or reboot, under a fresh shell. Stored under ~/.local/state; on by default, and you can turn it off or clear it.

Drop-in settings

systemd-style .conf directories: a distro or admin seeds defaults in /etc/secure-terminal.d, the user overrides them. Plain KEY=value.

Install

A standard Debian source package (Debian trixie). Pick a method; click to expand.

Run from a checkoutno install

Clone and run it in place, no packaging step:

git clone https://github.com/secure-terminal/secure-terminal cd secure-terminal ./usr/bin/secure-terminal # the GUI ./usr/bin/secure-terminal-cli # the sanitizing CLI wrapper

Needs PyQt6 (and python3-pyte for TUI mode), both in Debian main.

Build the Debian package.deb
git clone https://github.com/secure-terminal/secure-terminal cd secure-terminal dpkg-buildpackage -us -uc -b sudo dpkg -i ../secure-terminal_*.deb sudo apt-get install -f # pull dependencies (Debian main)

Then run secure-terminal for the GUI, or secure-terminal-cli to wrap a shell or command in the terminal you are already in.

Kicksecure repositorycoming soon

An apt install from the Kicksecure repository is planned, so updates arrive with your normal system upgrades. Not available yet.

Kicksecurecoming soon

secure-terminal is planned to be installed by default in Kicksecure, so a hardened system ships with a safe terminal out of the box. Not available yet.

Source codeAGPL-3+

Read, audit and build every line: github.com/secure-terminal/secure-terminal. Free Software under the AGPL-3+.

FAQ

The questions worth answering before you trust a terminal.

Is it Open Source? What licence?

Yes. secure-terminal is Free Software under the AGPL-3+, developed in the open on GitHub. Read the source, build it yourself, audit exactly what it does.

Does it send my data anywhere?

No. It runs entirely on your machine: no telemetry, no network calls, nothing about what you type or what your programs print leaves the terminal. The single exception is opt-in and explicit: if you enable the AI command hook, it sends the command line you are about to run plus recent scrollback to the model handler you configure (for example claude --print) so it can judge the command. That hook is off by default, you choose the handler, and nothing else is ever transmitted.

What platforms does it run on?

Linux. It ships as a standard Debian source package (Debian trixie and derivatives such as Kicksecure and Whonix); it is a PyQt6 application, so it runs anywhere PyQt6 and a pseudo-terminal are available. The secure-terminal-cli wrapper needs only Python and a POSIX pty.

Is it memory-safe?

Yes. It is written in Python (PyQt6); there is no C parser of untrusted output to overflow. The whole point is that untrusted bytes meet a small, auditable sanitiser, not a sprawling escape-sequence interpreter.

How is this different from my normal terminal?

A normal terminal interprets whatever a program prints, including attacker-controlled escape sequences, invisible characters and homoglyphs. secure-terminal does not: by default it parses no escapes, shows every character as itself (or a visible marker), and warns before a paste with hidden characters reaches the shell. See the comparison for the specifics.

Was this AI vibe coded?

It is an AI-assisted project - written and reviewed with the process documented at org-ai-assisted, with human review and AI review. The code is open for you to audit regardless of how it was produced - and the security claims are testable, not taken on trust.

Traditional terminal emulators have a live vulnerability class: merely viewing a file - cat a log, tail a server's output - can make the terminal type an attacker's text into your shell and run it, no keypress needed. It is not a historical footnote: the same class recurs, most recently as remote code execution in iTerm2 in 2024 (CVE-2024-38395), one of the twenty-odd real cases the terminal-poc-corpus collects. secure-terminal closes the whole class by design - CLI mode runs no escape parser and answers no query sequence, so output can never become input. See the comparison, measured with screenshots.