Skip to content

Add Fernet Encryption for Note Content in the Python Backend #16

@TheZupZup

Description

@TheZupZup

Overview

Context: Note content is currently stored as plain text in SQLite (typed_content column in the pages table, nexanote/storage/database.py). There is no encryption anywhere in the codebase. Ink stroke data (JSON in the strokes table) is also stored in plain text.

This issue implements symmetric encryption of note content at rest using the cryptography Python library (Fernet), which is already listed in requirements.txt.

What needs to be done

Step 1 — Key management

  • Generate a Fernet key on first backend startup and save it to a key file (e.g., nexanote.key in the data directory, permissions set to 600)
  • Load the key from the file on subsequent startups
  • Add a warning to the README: losing this key file means losing access to all note content

Step 2 — Encrypt/decrypt in the storage layer

  • In nexanote/storage/database.py, add two helper functions: _encrypt(text: str) -> str and _decrypt(text: str) -> str using the loaded Fernet key
  • In NexaNoteDB.save_page(), call _encrypt() on typed_content before writing to SQLite
  • In NexaNoteDB page read methods, call _decrypt() on typed_content after reading from SQLite
  • Encrypt stroke points JSON the same way (encrypt the JSON string before storing)

Step 3 — Migration

  • On startup, detect any existing unencrypted rows (try decrypting; if InvalidToken, the row is unencrypted) and re-encrypt them

Step 4 — Tests

  • Add tests in tests/test_models_and_db.py to confirm that raw SQLite content is ciphertext and that reading returns the original plaintext

Goal

After this change, opening the SQLite file with a plain text viewer shows ciphertext for all note content. The Flutter app sees no difference — it still receives decrypted content from the API as before.

What NOT to do (out of scope for this issue)

  • No passphrase input from the user yet (key file is enough for now)
  • No end-to-end encryption between Flutter and backend (that's a separate issue)
  • No encryption of notebook metadata (names, titles) yet

Where to look

  • nexanote/storage/database.py — only file that needs changing
  • requirements.txtcryptography is already listed
  • tests/test_models_and_db.py — add new tests here

Testing steps

  • Run the backend and create a note with content "hello world"
  • Open nexanote.db with a SQLite browser — verify typed_content is NOT "hello world"
  • Read the note via the API — verify it returns "hello world" correctly
  • Run python -m pytest tests/test_models_and_db.py — all tests pass

Related to: #8

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: Highcorehelp wantedExtra attention is neededsecuritySecurity-related features and improvements (encryption, data protection, key management).

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions