8000 feat(cache): encrypt the mail cache at rest with SQLCipher by hanthor · Pull Request #601 · hyprlab/hylki · GitHub
[go: up one dir, main page]

Skip to content

feat(cache): encrypt the mail cache at rest with SQLCipher - #601

Open
hanthor wants to merge 2 commits into
hyprlab:mainfrom
tuna-os:feat/at-rest
Open

hanthor wants to merge 2 commits into
hyprlab:mainfrom
tuna-os:feat/at-rest

Conversation

@hanthor
@hanthor hanthor commented Nov 5, 2026
Copy link
Copy Markdown
Contributor

Builds on #600 (its first commit is that PR's).

cache.db holds subjects, bodies, attachments, the Outbox and the search index in plain SQLite. This encrypts it at rest:

  • rusqlite uses bundled-sqlcipher (SQLCipher 4.5.7, linking the runtime's libcrypto; Cargo.lock is unchanged and the manifest needs no edit). The key is 32 random bytes in the keyring (cache-key), passed as a raw key so opening costs no KDF; a new key is read back before anything is encrypted with it.
  • Every connection sets cipher_memory_security = ON, reads once to catch a wrong key, and refuses a connection whose cipher settings differ from the pinned ones.
  • An existing plaintext cache is migrated in place: exported to a 0600 staging file, checked (quick_check, schema version, row counts, header), swapped in; any failure leaves the original byte for byte. Rows left in a WAL from a crash come across.
  • Fail closed: no key, a wrong key, a locked keyring or a failed migration never opens the cache in plaintext. A dialog offers Try Again, Quit, and Reset Cache (asks first; POP3 and unsent Outbox mail are lost). When the keyring is unreachable it offers Store Unencrypted instead of Reset, so someone with no keyring is not locked out.
  • Settings > Privacy > Stored Mail > Encrypt stored mail (on by default) decrypts in place at the next start when turned off, after a warning.
  • Demo mode and the cache timing test keep working.

Tests: wrong/missing/locked key fail closed with the file unchanged; migration from a plaintext cache with the real schema; no plaintext header or strings on disk; mismatched cipher refused; failed migration untouched; WAL rows survive; decrypt in place; reset. Checked live in a throwaway gnome-keyring under xvfb, and in a local Flatpak build. Not covered: local_contacts.db stays plaintext (0600).

The remote-content cache, the On This Computer tree, the exported log and the settings bundle were written with umask permissions, and the startup purge cleared /tmp while the Flatpak stages opened attachments in the runtime directory.
cache.db holds every cached message and attachment. It is now SQLCipher under a random key kept in the keyring, an existing cache is encrypted in place, and a missing, wrong or unreachable key asks the user instead of opening anything unencrypted. Settings can turn it off for a computer without a keyring.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0