Submarine

How your data is protected

We don't ask you to trust us. This page explains exactly what is encrypted, what our server can and cannot see, and how you can check every claim yourself.

In one minute

  • Your servers, logins, keys and notes are kept in one file on your device, encrypted with AES-256-GCM. A few choices for this device only, such as the look of the app and a folder shared with an RDP server, are kept unencrypted in the app's settings and are never synced.
  • The key is made from your profile password with Argon2id, a method that is slow on purpose so that guessing is expensive. The password is never stored or sent anywhere.
  • If you turn on sync, each item is encrypted on your device before it is uploaded. The server stores data it cannot decrypt.
  • The server does see the outside of your data: your email address, when you sync and from which IP address, how many items of each kind you have and how big they are, your profile names, and for shared profiles the share names and the members' email addresses.
  • The app has no ads, no analytics and no crash reporting.

The server cannot read

  • What is inside your items: names, addresses, ports, user names, passwords, private keys and their passphrases, tunnels, notes, commands, folder names
  • Which login or key belongs to which server
  • Who changed an item
  • Your profile password, your sharing passphrase and the keys that open your profiles

The server can see

  • Your email address, and your account password when you sign in (it stores only a bcrypt hash of it)
  • The IP address and time of sign-ins and syncs
  • How many items of each kind a profile has, their sizes, and when each one was changed and by which device (a random ID)
  • Your profile names
  • For shared profiles: the share names, the members' email addresses and their roles

What happens when you sync

Never leaves your device
Profile passwordArgon2idProfile keyAES-256-GCMEncrypted items
HTTPSSync serverStores encrypted items and the details listed above
The password and the keys stay on your device. Only encrypted items, and the details around them, are sent.

1. On your device

Each profile is one file. Inside is a small database with your servers, logins, SSH keys, notes and settings. The whole database is compressed and then encrypted with AES-256-GCM. A new random nonce is used every time the file is saved, and the file is written to a temporary copy first, so a crash cannot leave half a file behind.

The key comes from your profile password through Argon2id with 64 MiB of memory, 3 passes and 4 lanes, and a random salt for each profile. These settings are fixed in the app, so a changed file cannot make them weaker.

The password is never written to disk and never sent anywhere. After the key is made, the copy of the password in the app's core is wiped, and the key is wiped when you lock the profile. Locking also closes every connection, and calls off a login that is still in progress. If you forget the password, the profile cannot be opened. There is no reset, because nothing that could open it is kept anywhere.

Quick unlock is optional and off by default. On Windows it uses Windows Hello and the TPM, on Android the fingerprint sensor and the Android Keystore. The system keeps the profile's key, never your password, and the key is never synced. On Android, the system's cloud backup of the app's data is turned off.

On Windows, when Remote Desktop Connection opens a server that has a saved password, the app hands the password over in a short-lived .rdp file. The password in it is encrypted with Windows DPAPI for your Windows user, and the file is deleted as soon as Remote Desktop Connection has read it (at the latest after 30 seconds; a file left behind by a crash is removed at the next start).

2. Cloud sync

Sync is off until you sign in. Each profile has its own random 256-bit data key, which lives inside the encrypted profile. Before upload, every item (a server, a login, a key, a note…) is encrypted on its own with this key, using AES-256-GCM and a new random nonce.

Next to each encrypted item, the server gets a random ID, the kind of item, a timestamp, and a flag that says whether it was deleted. The timestamp contains a random ID of the device that made the change. The size of the encrypted item shows roughly how big the item is. The server also stores each profile's name. Known host keys, command history, monitoring settings and the choices kept for this device only are never synced.

This is what the server stores for one item: (Example)

{
  "uuid": "6f1c2e0a9b7d4c3e8a5f0d1b2c3e4f50",
  "entity_type": "servers",
  "updated_at": "001761043562118:00000:9f3a61c2d4e5b7a8",
  "deleted": false,
  "blob": "4e8a1f0c2b9d3e7a5c6f8b1d0a2e4c6b8d0f1a3c5e7b9d1f…"
}

3. Opening a profile on a new device

To open a synced profile on another device, that device needs the profile's data key. So the server keeps one more record: the data key, encrypted with a key made from your profile password, with the same Argon2id settings. The server cannot open it.

This is also the main thing an attacker could try if they ever stole the server's data: guess your profile password offline. Every guess costs a full Argon2id run, so a long password that you use nowhere else keeps your data safe.

4. Your account password

Your sync account has its own password, separate from your profile password. It is sent to the server when you sign in, inside HTTPS, and the server stores only a bcrypt hash of it. After 5 wrong passwords, password sign-in pauses for 15 minutes.

The service sits behind Cloudflare, which handles the HTTPS connection for it. So, like the server, Cloudflare could see your account password when you sign in, but your items only in their encrypted form.

Never use your profile password as your account password. Then nothing the server or Cloudflare ever sees can open your profiles.

5. Sharing a profile

To share profiles, each account has an X25519 key pair. The private key is encrypted with a key made from your sharing passphrase (Argon2id again), and the passphrase never leaves your device. Each member of a shared profile gets the profile's data key sealed to their own public key.

When you invite someone, the app uses the public key that the server gives for that email address. If a member's key changes later, the app tells you and shows a fingerprint you can compare with them. The server knows the names of shared profiles and who is a member, but not what is inside.

6. If the server were hostile

Good security is not only about what a server is supposed to do. Here is what someone in full control of the server could and could not do.

They could not

  • Read your items, or change what is inside them without it being noticed
  • Open the stored copy of your profile key without your profile password
  • Learn your profile password or sharing passphrase, which the app never sends
  • Change the app on your device: there are no automatic updates, the app only shows that a new version exists

They could

  • Try to guess a weak profile password offline, one slow Argon2id run per guess
  • Catch your account password the next time you sign in
  • Delete items, bring back older versions, or hold changes back. Before a sync deletes anything, the app keeps a copy of your profile on your device
  • Hand out the wrong public key when you invite someone to a shared profile
  • See the details listed in the table above

7. Everything the app connects to

Your own servers, and the proxies and jump hosts you set up for them. The sync server, only when you are signed in. And GitHub, to check for a newer version: when the profile list opens and when you press “Check for updates”, the app asks GitHub's public list of releases. That request carries only the app's name and version.

There is no analytics, crash reporting or advertising code in the app. The app trusts only its own built-in list of certificate authorities for sync, and it never follows a redirect, so it cannot be quietly pointed at another server.

8. What a remote desktop gets from your device

The clipboard. Text you paste into an RDP or VNC tab goes to that server, and nothing is offered to it before you paste. With Share the clipboard on (the default, and a switch for each server), text copied on the remote desktop is put on this device's clipboard. It is text only, never files, and VNC carries plain ASCII text only.

Files, only with the built-in RDP client on a computer, and only if you choose a folder or a drive for that server, read only if you like. The server sees it as a drive under \\tsclient, and nothing else of your computer. Every path it asks for is checked: no .., and no link is followed on the way, so nothing outside what you chose can be reached.

These choices are kept on this device and never synced, so nobody you share a profile with can turn them on.

Check it yourself

You don't have to take our word for any of this. Here are ways to check, from easy to advanced.

  1. Read the code

    The app is open source. The links on this page go to the exact lines that do the work, at the version this page describes (c9dec31).

  2. Watch the connections

    Use a firewall or network monitor. You will see your own servers, the sync server only while you are signed in, and api.github.com when the profile list opens. Nothing else.

  3. Look at what the server stores

    Advanced: take your sign-in token from the app's data folder (cloud_token.json) and ask the API for your data, with the header X-Auth-Token: GET /api/sync/profiles, or POST /api/sync with an empty list of records. You get back only IDs, kinds, times, flags and encrypted text. Sign out in the app afterwards to end that session.

  4. Read the traffic

    Advanced: run a small proxy on your computer that passes requests on to the real server, and set the app's sync server to http://127.0.0.1 and the proxy's port. The app allows plain HTTP only for your own computer. You will see what the app sends, and that your items are encrypted. (A proxy certificate added to your system will not work, because the app uses its own list of certificate authorities.)

  5. Decrypt your own data

    Advanced: with your password, you can open your own data with any Argon2id and AES-GCM tool, using the settings in the table below. Without the password, nobody can.

  6. Check your download

    Compare the checksum with SHA256SUMS in the release (from version 0.4), check the Android app's certificate with apksigner, or build the app from source.

The settings, for your own tools

Key derivationArgon2id, version 0x13, 64 MiB memory, 3 passes, 4 lanes, 32-byte key, 16-byte random salt
Profile file“OMNV” · version 1 · salt (16 bytes) · nonce (12 bytes) · AES-256-GCM of the zstd-compressed SQLite database, with its 16-byte tag
Synced itemhex(nonce (12 bytes) · AES-256-GCM ciphertext and tag), with the profile's 256-bit data key
Stored copy of the profile keyhex(0x02 · salt (16 bytes) · nonce (12 bytes) · AES-256-GCM of the 32-byte data key), with the key made from your profile password
SharingX25519, HKDF-SHA256, AES-256-GCM

What this page does not promise

  • Submarine has not had an independent security audit. Its reviews so far were done by the author.
  • A weak or reused password is the weakest point. Use a long, unique profile password, and a different account password.
  • Telnet and plain VNC are not encrypted by design. The app labels them, and can run them through an SSH server. Over plain VNC, clipboard text you paste or the server copies crosses the network unencrypted too.
  • The built-in RDP client is experimental (it is built from an IronRDP commit, not a release) and is off until you choose it for a server.
  • This page describes the app at c9dec31. If the code changes, this page changes with it.