Security at Termalin
Termalin is an SSH client: it holds the most sensitive credentials you own. This page explains — plainly — what our servers store, what they can never read, and why you don't have to trust them much.
End-to-end encryption, concretely
Everything meaningful is encrypted on your device before it is sent anywhere, using a key derived from your master password (PBKDF2-HMAC-SHA256, 600,000 iterations → AES-256-GCM). The master password never leaves your device. We cannot read your data, and we cannot reset it for you — that is the design, not a bug. Your backstop is yours to hold: set an optional recovery code and you can get back in if you ever forget the password; skip it, and a forgotten password means the data is gone. Either way, the power to recover stays with you, never with us.
What our servers see
| Data | Server can read? |
|---|---|
| Your hosts, SSH keys, passwords, snippets (sync) | No — encrypted blob only |
| Terminal session recordings (contents) | No — encrypted blob only |
| Your master password | Never sent |
| Account email + password | Email yes; password stored as an Argon2 hash |
| Recording title and host label | No — encrypted with your master password, like the recording |
| Recording metadata (duration, size, whether an AI agent acted) | Yes — used for lists & quotas |
| Subscription tier, device names, timestamps | Yes |
| Web terminal sessions (the browser cabinet) | Pass through our server while live; not stored. The host, port and user name are logged |
| Remote desktop sessions (RDP / VNC) | Pass through our server in memory while live; not stored. The host and port are logged; the RDP certificate is pinned on first use. Direct sessions from the desktop app don't touch our servers at all |
| Server backup archives (Connector servers) | No — encrypted for a key only you hold before they are stored; while an archive is being encrypted the stream passes through our backend in memory |
| Commands AI apps run through the hosted MCP endpoint | Yes — the command text is in your activity log; its output is kept only if you turn that on, encrypted to a key only you hold |
| Server metrics and alerts from Connector servers | Yes — needed to draw charts and raise alerts (see the privacy policy) |
| Key-derivation salt | Yes — a salt is not a secret; it lets the web cabinet derive your key in your browser |
Updates can't be forged
Auto-updates are signed with an offline Ed25519 key; the matching public key ships inside the app. Before installing anything, the app verifies the release manifest's signature and the installer's SHA-256 against it. Our own server is not a trusted party here: even a fully compromised update server (or a DNS/hosts redirect) can withhold updates — it cannot inject one.
The app authenticates the server, not just TLS
Before sending your account credentials anywhere, the app challenges the server with
a random nonce; the server must sign it with its identity key, verified against a
public key embedded in the app. A fake backend — reached via a hosts-file
or DNS redirect, or a machine set to trust an extra TLS certificate — cannot
produce that signature, so it never sees your password.
The app checks termal.in's certificate
The desktop app compares termal.in's certificate against a short list of public keys built into the app — the roots of the certificate authorities our hosting uses — rather than any of the hundred-plus authorities a system trusts. In this release it only logs a mismatch and never blocks: a mismatch is noted and its count is shown in Settings, while the request itself goes on with the system's own check, so a network that inspects encrypted traffic won't lock you out. Refusing such connections is switched on later, through a list signed with our offline key. You can turn the check off in Settings → Security ("Check the termal.in certificate").
No secrets in the binary
The desktop app embeds only public keys. Inspecting the app's files yields nothing usable. Anything paid is enforced server-side.
The web cabinet, honestly
The web cabinet decrypts recordings in your browser with your master password — the password never leaves the page. The honest caveat every web-E2E product shares: the server delivers that page's code, so a compromised server could serve a modified page. The desktop app, with its signed updates, remains the highest-trust path; the cabinet is for convenience.
On your machine
- SSH host keys are pinned on first use (TOFU) in
~/.termalin/known_hosts. - Your termal.in account supports two-factor authentication (TOTP with backup codes — each authenticator code works only once) and passkeys — the desktop app can sign in through the browser, so the password never touches the app.
- Recordings store terminal output and event markers — never raw keystrokes.
A password typed at a hidden prompt (
sudoand the like) is never echoed, so it never lands in a recording; anything the terminal prints is part of the tape. - Recordings saved on your device are encrypted (AES-256-GCM) with a key held by your OS keystore — the macOS Keychain, the Linux Secret Service, or Windows DPAPI — so the files alone don't reveal what was on screen. Recordings from older versions are converted the first time the app sees them.
- "Remember master password" (optional, off by default) is protected by your OS keystore — Windows DPAPI, the macOS Keychain, or the Linux Secret Service — bound to your OS user.
Infrastructure
The backend runs in Docker behind Cloudflare, with TLS 1.2+ (Let's Encrypt certificates). The database is reachable only from the host's localhost — never exposed to the internet — and backups are encrypted. We collect as little as possible: no analytics SDKs, no ad trackers, and we don't sell data.
Compliance & roadmap
We'd rather tell you exactly where we stand than imply more than we've done:
| Area | Status |
|---|---|
| AES-256-GCM vaults, PBKDF2-HMAC-SHA256, Ed25519-signed updates | Shipped |
| Data minimisation + full account/data deletion on request (GDPR posture) | Shipped |
| Independent third-party penetration test | Planned before GA |
| SOC 2 | On the roadmap — ask where it stands |
Reporting a vulnerability
Email security@termal.in (see our security.txt) with what you found, how to reproduce it, and what you think the impact is. We acknowledge within 3 working days and tell you what we decided — including when we decide it is not a problem, and why.
Please test against your own account only, stop at proving the issue (no data exfiltration, no persistence, no denial of service, no third-party or user data), and give us a reasonable window before publishing. Work inside those lines and we will not pursue you for it.
We do not pay bug bounties. Termalin is a small pre-revenue project and there is no reward budget; we would rather say so up front than let anyone spend a weekend on spec. What we do offer: credit in our security.txt acknowledgements under the name and link you choose, a written reference describing your report, and a Pro licence for a few months. If the finding is serious, we will also tell you when the fix ships.
Acknowledgements
Our thanks to the researchers who reported issues responsibly and helped us make Termalin safer:
- MD Rabbi Hossain — OAuth client-registration hardening in the MCP authorization flow (Sept 2026).