Home / Security

Claims, limits, and how to check both

A privacy promise is only worth what you can check. Here is every claim the app makes, the attacker each one is meant to stop, and — with equal weight — where the protection ends.

No external audit

Everything below was designed, built and tested by one person. Treat the claims as carefully tested intentions until someone independent has looked (L-09).

Closed source, open to inspection

The source isn’t distributed, but the license explicitly lets you decompile, instrument, fuzz and packet-capture the app — and publish what you find (L-17).

No recovery, by design

There is no back door for anyone, including you. Forget your passphrase or lose your only key and the vault cannot be opened (C-02).

What it promises

Sixteen claims, each tied to evidence

In the project, every claim is enforced by a ledger: a claim with no evidence carrying it — a test, or for the dependency pins a verification script — fails the build. Each lists the adversary it is meant to hold against.

IdClaimHolds against
C-01Everything is encrypted at rest. Messages, titles, personas, memories and settings are sealed with AES-256-GCM under a key derived from your factors. The vault file contains no readable content.Anyone who gets the file: a thief, a repair shop, a forensic examiner, a cloud sync you never asked for.
C-02No master key, no recovery, no escrow. Your passphrase and/or hardware key are the only way in. Lose them and the vault is gone — for you, and for everyone else.The author, anyone who compels the author, anyone who compromises a build.
C-03Refusals give nothing away. A wrong passphrase, a wrong key and a file that is not a vault all look the same to the app. Nothing in the refusal says how close a guess was.Someone probing a file to learn whether a guess was close.
C-04Nothing it ships can reach the network. No assembly references a networking API, and no bundled native library — SQLite, Skia, HarfBuzz, llama.cpp and its backends — imports one. There is no telemetry and no update check.Telemetry, update checks, exfiltration by a compromised dependency.
C-05The only file outside a vault is a crash log. It holds an exception type and a stack scrubbed of file paths, your account name, and anything long enough to be diary content.A forensic examiner reading what the app left on the host.
C-06A vault is exactly one file. No journals, no -wal/-shm sidecars, no temp copies. Lock it and it can be moved immediately.Leaving fragments behind on a shared or borrowed machine.
C-07Locking zeroes the key. Key material is overwritten where it lived, not merely dereferenced.Someone who dumps process memory after the vault is locked.
C-08Records are bound to their rows. Ciphertext that is moved, copied or swapped between rows fails authentication instead of being read as if it belonged there.An intruder rewriting a diary using its owner’s own ciphertext.
C-09Integrity checks cover everything. The integrity check authenticates every encrypted record, finding them by asking the container rather than trusting a hand-kept list.Silent bit rot, tampering, and a checker that drifts out of date.
C-10Hostile files can’t hurt the app. A damaged or malicious vault cannot crash or hang the app, exhaust memory, inject SQL, or produce unauthenticated content.A vault from somewhere untrusted — a shared drive, an attachment, a restored backup.
C-11Backups are verified before they count. A backup is a full vault with every property above, and it is read back and verified before it can replace anything.A backup that looks fine and isn’t, discovered on the day you need it.
C-12No downgrade path. An older vault is upgraded on first open and never run under older, weaker rules — and editing its header can’t talk the app into it.An attacker downgrading a vault to a weaker format.
C-13Dependencies are pinned and audited. Every dependency, transitive ones included, is pinned by version and content hash, and audited for known vulnerabilities on every verified build.A library that changes underneath a build, or a known-vulnerable one shipping unnoticed.
C-14Record sizes are blurred. Every record is padded to a 256-byte bucket before sealing. Every message from 1 to about 160 characters is stored at exactly the same size.Reading the shape of a diary out of a file you can’t decrypt.
C-15Changing how a vault opens is safe. Changing unlock factors requires the current ones, and every new slot is proven to open the vault before the old arrangement is replaced. A missing enrolled key fails the whole change.Someone who finds your vault unlocked and makes themselves the only way back in.
C-16Memories stay in their conversation. A memory is recalled only in the conversation it came from unless you mark it for all conversations. One setting, off by default, lifts that across the diary.The private conversation surfacing in the everyday one, in front of whoever is watching the screen.
Where it stops

What it does not protect you from

These aren’t fine print. Each is the consequence of a deliberate decision, written down so you can decide whether the trade is right for you.

L-01The file’s size, record counts and modification time are visible without a key.
L-02The header is plaintext by design: format version, cipher and KDF parameters, slot types, and enrolled key ids. None of it opens anything.
L-03While the app runs, decrypted text lives in ordinary memory and can outlast locking — it can reach the page file, hibernation image or an OS crash dump.
L-04Passphrases pass through .NET strings, which cannot be zeroed.
L-05Replacing the whole vault with a genuine older copy of itself cannot be detected.
L-06Model (GGUF) files stay wherever you put them, unencrypted. Only the path lives in the vault.
L-07No hidden volume and no deniability. A vault is recognizable as an encrypted file.
L-08The bundled native libraries are third-party code: their imports are scanned, their behavior is not audited.
L-09No external security audit has been performed.
L-10Nothing here defends against a compromised OS, a keylogger, screen capture, malware running as you, or someone looking over your shoulder.
L-11The bundled .NET runtime contains socket code, as every general-purpose runtime does. The app provably never references it.
L-12A vault is an ordinary SQLite database: tools can see its table names, row counts and record sizes — never its content.
L-13Deleting an entry overwrites it in the vault file, but can’t reach a fact the AI already distilled from it, copies of the file made earlier, or encrypted leftovers of the app’s write journal on your disk.
L-14Embedding vectors are a lossy but partly invertible copy of your text. They are encrypted like the text and never exported.
L-15Keeping a memory in its conversation governs what the AI is shown, not how the vault is partitioned. The memory manager lists everything.
L-16A very long memory is semantically indexed only for its first ~3,000 tokens. It is stored in full, but may be condensed or shortened to fit when it is recalled.
L-17The source is not published, so test-backed claims rest on the author’s own test runs. The black-box checks below don’t.

Worth doing if the file’s shape matters to you: keep your vault inside an encrypted container that is mounted only while you use it — a VeraCrypt volume or a BitLocker-encrypted VHDX. Whole-disk encryption alone doesn’t hide the vault from programs running while you’re logged in. Full-disk encryption does, however, cover the page file and hibernation image (L-03).

Don’t take our word for it

Verify it yourself

These checks treat the app as a black box. None of them needs the source code, and none asks you to trust anyone.

Your words aren’t in the file

Create a vault, write a distinctive phrase, lock it, and search the raw bytes. Expect nothing.

Select-String -Path .\diary -Pattern "the phrase you wrote" -Encoding utf8

Open it with sqlite3: you’ll see tables and a plaintext header describing how it is locked. Every content column is an opaque blob.

sqlite3 diary "SELECT id, length(data_enc) FROM messages;"

It never touches the network

Windows: block it outbound, use it normally — load a model, chat, back up, lock — then check the firewall log. There are no blocked attempts, because there are no attempts.

New-NetFirewallRule -DisplayName "MTS verification" -Direction Outbound `
  -Program "C:\Program Files\My Thought Safe\MyThoughtSafe.exe" -Action Block

Linux: run it in a network namespace with no interfaces at all.

sudo unshare -n ./MyThoughtSafe-0.10.0-x86_64.AppImage

Record sizes are blurred

Write several short messages of very different lengths, then group by stored size. They collapse onto a single row.

sqlite3 diary "SELECT length(data_enc), count(*) FROM messages GROUP BY 1;"

Subtract 28 bytes of nonce and tag from any stored length: what remains is a multiple of 256.

Changing factors really changes them

In Settings → Security, change a test vault from a passphrase to a passphrase and a security key. Lock it, then try the passphrase alone (refused), the key alone (refused), and both (opens).

Try the change with a wrong current passphrase: it must refuse, and the vault must open exactly as before.

Nothing is left behind

Run the portable build from a USB stick, use it, close it. %LOCALAPPDATA%\MyThoughtSafe (or ~/.local/share/MyThoughtSafe) should not exist unless it crashed — and then only crash.log, with paths and your name replaced by <redacted>. No registry keys, no recent-files list, no cache.

You have the published binary

Compare your download’s SHA-256 against the published checksums. A match says the file arrived exactly as published. It says nothing about what’s inside — which is why the license lets you open it up.

Get-FileHash .\MyThoughtSafe-0.10.0-setup.exe -Algorithm SHA256

Found something? That is the most valuable thing anyone can contribute to this project. The license explicitly permits publishing it, unflattering or not.