How Conchshell stores credentials and verifies host keys

What the vault does, what it refuses to do, and where the boundary sits.

The vault

Data is sealed with XChaCha20-Poly1305 under one random data key. Every unlock method — the system keychain, a master passphrase stretched with Argon2id, a recovery code — gets its own key slot wrapping that same data key. On a Mac, iPhone, iPad or Android phone the vault opens with the system keychain (on Android, the Keystore) and needs no master passphrase. On Windows and Linux the app does not use a system keychain, so the vault takes a master passphrase of at least 12 characters, asked at every launch, and comes with a recovery code. Adding or revoking a method rewrites one slot; the vault itself is never re-encrypted.

On iPhone and iPad the vault uses the same XChaCha20-Poly1305 and Argon2id; its key is kept in the iOS Keychain and out of iCloud Keychain.

The interface can create a credential and delete a credential. It cannot read one back. Not because reading is blocked, but because no command exists that would answer that question.

The vault is not created behind your back. Whenever the app starts with a saved password still unencrypted, it offers to create the vault; until you accept, saved passwords stay unencrypted in the application's local data. Accepting moves them into the vault, and each unencrypted copy is erased only after the vault has confirmed it holds that password.

passphrase → Argon2id → slot key → data key → XChaCha20-Poly1305 → vault

Route binding

A credential kept in the vault is bound to the full route it is first used on: host, port, user, proxy and jump host. Change any of them and the credential is not offered — the connection asks you instead. A window that has been tampered with cannot walk a saved password to a different server, because the vault will not hand it over for any other route. Passwords that stay unencrypted outside the vault get no such binding.

Host keys

First connections and changed keys are confirmed in a dialog drawn by the operating system, outside the application's own surface. The application cannot repaint that dialog, cannot click it, and cannot make a key replacement look like a first connection. This is the one prompt worth reading carefully, so it is the one prompt the application does not draw.

RDP and FTPS server certificates are pinned too. A certificate the system's trust store vouches for — a valid chain, the name you typed, current dates — is accepted without a question and pinned. Any other certificate shows its fingerprint and details on the first connection for you to confirm, and once you have pinned it, its chain, name and dates no longer matter. A changed certificate is not used until you confirm the replacement in the same OS warning dialog, unless both the old pin and the new certificate were vouched for by the system. Android has no system check: there every certificate is confirmed by you. Compare the fingerprint on that first connection.

On iPhone and iPad the host key is checked on every connection: the first time, you see its fingerprint and decide; if it changes, the connection stops before sign-in and shows the old and new fingerprints. An RDP or FTPS certificate the system does not trust is shown to you to decide.

Closing this dialog without pressing the trust button — Escape, the close box, Alt+F4, Back on Android — counts as refusing, on every platform. On a phone, a question left unanswered for 10 minutes is a refusal too. Version 2.16.0 and earlier had a fault here: on Windows and Linux, closing the dialog counted as trusting the key, and on Android so did Back; 2.17.0 fixed it.

Updated 2026-11-02 · v2.17.1