Proposal: SQLCipher-backed encrypted profile for desktop local database protection

Hi Joplin maintainers,

I have implemented a working prototype for desktop-side local profile protection and would like to ask whether this direction is acceptable before opening any pull request.

I am aware of the current pause on PRs from new contributors, so I am not opening a PR directly. This post is for direction review first.

Prototype branch:

Prototype commit:

8d494187b56b8924af1ab2e894a3d52b90beed69

Motivation

A desktop app lock alone does not provide meaningful protection if the local Joplin profile can still be read directly from disk. This was also pointed out in the desktop lock discussion: on desktop, local data encryption is needed for a stronger privacy model.

The prototype therefore focuses on database-at-rest protection for the desktop profile, with App Lock as a secondary UI-layer feature.

What the prototype implements

  • Desktop App Lock

    • startup lock
    • idle lock
    • manual lock command
    • command/navigation guard while locked
    • password hash and cooldown handling
  • Encrypted Profile

    • startup gate before opening database.sqlite
    • SQLCipher-backed encryption for database.sqlite
    • profile-encryption.json metadata
    • scrypt-based password key derivation
    • AES-256-GCM wrapping of the generated database key
    • migration from plaintext database.sqlite to encrypted SQLCipher database
    • plaintext backup retention during migration
    • migration failure handling and rollback path
    • backup deletion UI
  • Database integration

    • optional databaseKeyHex passed into application startup
    • SQLCipher PRAGMA key injection in the database driver
    • database readability verification after key injection
  • Tests

    • encrypted profile metadata/key wrapping tests
    • migration and rollback tests
    • backup handling tests
    • desktop startup scheduling tests
    • App Lock reducer/service tests
    • SQLCipher E2E test

Current protection boundary

This is not full-profile encryption.

The current prototype protects:

  • database.sqlite, after migration has completed and SQLCipher is available.

It does not currently protect:

  • resources/
  • attachments
  • settings files
  • cache
  • logs
  • plugins
  • plugin data
  • temporary files
  • the plaintext migration backup until the user deletes it

The UI warns users about this boundary before enabling the feature.

Validation already performed

On the prototype branch:

  • yarn install --immutable passed
  • yarn workspace @joplin/lib tsc passed
  • yarn workspace @joplin/app-desktop tsc passed
  • encryptedProfile lib tests passed
  • encryptedProfile desktop tests passed
  • App Lock tests passed
  • SQLCipher E2E test passed
  • desktop build passed

Question

Would this direction be acceptable for Joplin Desktop?

If yes, I can split the prototype into smaller reviewable PRs, for example:

  1. SQLCipher capability probe and native module loading
  2. encrypted profile metadata and key wrapping service
  3. optional database key support in DatabaseDriverNode
  4. plaintext-to-SQLCipher migration helper
  5. desktop startup unlock gate
  6. settings UI
  7. App Lock as a separate follow-up

I mainly want to know whether SQLCipher-backed local database-at-rest encryption is an acceptable direction before spending time splitting this into upstream-ready patches.

I have implemented a working prototype for desktop-side local profile protection and would like to ask whether this direction is acceptable before opening any pull request.

Probably not. This is a massive pull request that would require a long term commitment from you to maintain it, and as you are new here we can't know if you're ready to do this.

Also see this post: