GSoC 2026 Proposal Draft – Idea 7: Support for encrypted notes and notebooks – moazhashem
Links
Project Idea: Support for encrypted notes and notebooks
GitHub: Pixels57
Pull Requests for Joplin:
- Desktop: Resolves #14621: Application crashes when deleting a notebook (Merged).
- Desktop: Resolves #14797: Completed date/time is shown as a number (Merged).
- Desktop: Resolves #14763: Add search for settings in the desktop app (Open).
1. Introduction
I’m Moaz Hashem, I’m a senior computer engineering student at Cairo University, expected to graduate in 2026. I have hands-on experience in Node.js, TypeScript, Java, C++, Python, advanced database design: ACID guarantees, concurrency control, and vector databases for semantic search, cryptography, and security. I’ve built a lot of Software Engineering, AI, Security projects.
Personal Projects:
-
Reddit-style app with Node/Express/MongoDB, real-time features, Jest tests
-
Ransomware detection static analyzer (Python, static analysis, ML), and worked with AES-256 and ChaCha20 to implement a ransomware
-
Web app with defenses against SQLi, XSS, CSRF, and broken auth.
And a lot of other projects on my GitHub.
I apply SOLID principles and care about testable, maintainable code. I haven’t contributed to open-source before but looking forward to contribute to Joplin. Also I am planning to build a career in cybersecurity so I am passionate about it, and have always wanted to contribute to open-source projects.
2. Project Summary
What Problem it solves:
Joplin today protects note content in the cloud mainly through sync end-to-end encryption (E2EE): one master password for the whole vault, and encryption is effectively all-or-nothing at the sync layer. Users cannot give individual notes or notebooks their own passwords, and E2EE is not enabled by default , which limits how people can protect especially sensitive notes on a shared or unlocked device.
This project adds optional local “lock” encryption at the note level with notebook level independent of whether sync E2EE is on. It is informed by community discussion around stronger defaults and clearer encryption stories.
What will be implemented:
Note and notebooks bodies will use AES-256-GCM, with keys from PBKDF2 (e.g. HMAC-SHA256), a salt, and documented iterations. Passwords and raw keys are not stored only salt, ciphertext, and crypto metadata (IV, tag, KDF details as needed) in SQLite database.
Encrypt on save while locked, decrypt in memory after a correct password, wrong password or tampered data fails via GCM verification.
UI: context-menu lock/unlock, password dialogs, lock icons in list and tree, optional idle timeout for in-memory keys. FTS will skip locked content, like today’s encrypted notes.
Expected outcomes:
Users can optionally protect specific notes whole notebooks with separate passwords, with plaintext bodies not stored in the DB while locked. The feature remains compatible with existing sync E2EE behavior. Tests and docs reduce regressions and help future contributors.
3. Technical Approach
Data model and storage
We are going to add 3 columns to Notes table and 2 to Folders table:
-
lock_enabled:INTEGER NOT NULL DEFAULT 0(0 = unlocked, 1 = locked). -
salt: nullableTEXT, base64-encoded random bytes (16–32 bytes fromrandomBytesincrypto.ts -
lock_cipher_text: nullableTEXT, per-note lock ciphertext (encoding aligned withEncryptionResultintypes.ts), and this is not addded to Folders table. -
encryption_applied/encryption_cipher_text: unchanged.
No password or key is persisted, only salt, lock_cipher_text, and lock_enabled on disk. When sync E2EE is on, the whole row is re-encrypted with the master key for upload (double encryption), the server sees only the outer E2EE blob.
Crypto
Reuse the existing layer: Crypto interface and EncryptionResult type in types.ts, PBKDF2 + AES-GCM in crypto.ts.
A new module will be added packages/lib/services/perNoteLock/crypto.ts, that will have functions deriveKey(password, salt), encryptBody(plain, password, salt), decryptBody(cipher, password, salt), Pass salt as decoded bytes or base64 string consistently with how it is stored in the column.
EncryptionService.ts remains unchanged for master-key sync.
Load and Save
Load:
-
In
useFormNote.tscallsNote.load(noteId)beforeinitNoteState.-
If the note or its parent folder is locked, show an "Enter password" dialog, on correct password derive the key, decrypt, and call
initNoteStatewith plain content. -
Optionally cache the derived key or decrypted content in memory for the session, clear on lock or app close, or clear on timer.
-
Save:
- Extend the
Note.save()path inNote.ts, if the note is locked, encrypt the body with the password-derived key before writing, Never write plaintext tobodywhilelock_enabled = 1.
UI
-
Context Menu: Add "Lock Note" button to Notes List Context Menu and "Unlock Note" button to Notes List Context Menu. Add "Lock Notebook" button to Folder Tree Context Menu and "Unlock Notebook" button to Folder Tree Context Menu. All context menus are triggered from useOnContextMenu.ts.
-
Dialogs: "Set password" and "Enter password" dialogs that mirror the
MasterPasswordDialog, registered inappDialogs.tsx. -
List and Tree: Add icons indicating each Note/Notebook's Lock status in the
NoteListItemandNoteList2.tsxfiles and images in the Folder Tree.
Search
SearchEngine.ts excludes notes from the Full-Text Search (FTS) based on encryption_applied = 1. The same exclusion will apply to Locked Notes. In order to FTS Trigger and Query to Index/Select a Note for FTS the Index/Selection Query must include lock_enabled = 0. Any references to JoplinDatabase.ts that are indexed contain plaintext body for Locked Notes must be modified to remove the indexing.
Locking Logic
Clear the in-memory key/decrypted value for a note if there have been N minutes of inactivity following the last use. There will be no new database storage, the in-memory cache will utilize a timer and timestamps.
E2EE By Default Migration (issue #14465 alignment)
Dialog.tsx: add a prominent warning when setting the master password and an optional "Forgot password?" path using resetMasterPassword() from utils.ts.
Documentation and Testing
A developer doc will cover: where per-note data is stored, how the key is derived, and E2EE double-encryption interaction. Written incrementally alongside the implementation.
Unit tests will be added alongside each implementation phase, following existing repo patterns (Jest, test files next to the code under test). They will cover all implemented functionality to ensure correctness and prevent regressions.
Decisions (for maintainer sign-off)
Folder lock and children (we have 2 options):
- Lazy encryption: folder row has
lock_enabled+salt. Descendants are effectively locked by inheritance. Each note’s body is encrypted with the notebook password-derived key when it is next saved (or explicitly locked). No mandatory bulk re-encrypt on folder lock, avoids large sync spikes. - Immediate bulk: locking a folder encrypts every descendant note in one operation. Heavier; may need progress UI and conflict handling.
Title and FTS when body is locked (we have 2 options):
- A: exclude note from FTS entirely (title and body), same as
encryption_applied = 1. Strongest privacy, user cannot search locked notes by title. - B: Keep
titleplaintext in DB and index title only in FTS for locked notes. Better findability, weaker privacy for titles.
This is a block diagram that demonstrates the proposed architecture
4. Implementation Plan
Community Bonding Period (May 1 - 26):
Week 1 (1 - 7 May):
- Community bonding: Set up dev environment, sync with mentors, clarify proposal ambiguities, familiarize with codebase and architecture, draft design notes, Learn more about Joplin as an organization with a mission and vision.
Week 2 (8 - 14 May):
- Data model & crypto: Database support for lock fields, crypto helper (password + salt -> encrypt/decrypt note body) with tests.
Week 3-6 (15 May - 11 Jun):
- Final exams and graduation project submission. No implementation. Remain active in the community, respond to mentor messages, follow discussions.
Week 7 (12 Jun- 18 Jun):
-
useFormNote.ts: (Load & save) Implement the note load and save paths for locked notes, with unit tests.
-
optional in-memory cache
Week 8 (19 Jun - 25 Jun):
-
End-to-end lock/unlock Flow, Context menu in NoteListUtils: "Lock note" / "Unlock note". "Set note password" and "Enter password" dialogs, (MasterPasswordDialog-style)
-
Lock icon in note list (NoteListItem, NoteList2)
Week 9 (26 Jun - 2 Jul):
-
Folder tree: Lock notebook / Unlock notebook, Lock icon for locked notebooks, Polish dialogs and copy.
-
In SearchEngine.ts: exclude locked body from FTS, JoplinDatabase.ts: update FTS triggers.
-
Optional: lock-after-timeout.
Week 10 (3 Jul - 9 Jul):
- Per-notebook encryption: Folder lock: flag + salt in folder. Unlocking the notebook unlocks all notes in it. Tests for notebook lock/unlock, ensure all tests pass.
Midterm Evaluation (Jul 6 - Jul 10)
Week 11 (10 Jul - 16 Jul):
- E2EE migration & docs: MasterPasswordDialog/Dialog.tsx: prominent warning when setting master password. Optional "Forgot password?" path using resetMasterPassword() (e2ee/utils.ts). Developer doc. Open PR and request reviews.
Week 12 (17 Jul - 24 Jul):
-
Remaining fixes, final PR(s), changelog
-
Preparation for final evaluation
-
Seeing if there is additional work to be done and seeking reviews before the final
5. Deliverables
-
Per-note encryption
- Add optional per-note locking so a note is encrypted at rest with a password-derived key and only decrypted in memory after successful authentication.
-
Per-notebook encryption
- Extend the same model to notebooks so all notes inside a locked notebook follow the same protection and access flow.
-
User interface and workflow
- Provide lock/unlock actions, password prompts, and visible locked-state indicators in the desktop UI so protected notes and notebooks are easy to manage.
-
Reuse of existing crypto stack
- Build the feature on top of Joplin’s existing encryption primitives and types to avoid duplicating crypto logic and keep the design aligned with the current codebase.
-
Search and indexing protection
- Ensure locked note bodies are excluded from local search and full-text indexing so encrypted content is not exposed through plaintext search results.
-
Sync E2EE by default (optional)
- Explore enabling sync E2EE by default with a safer onboarding flow, including clearer master-password setup and warning messages.
-
Testing and documentation
- Add targeted tests and technical documentation covering encryption flows, locked note behavior, and expected interactions with the existing sync E2EE model, in order to have clean, reliable, and maintainable code.
6. Availability
-
Weekly availability: 30 hours per week
-
Time zone: (GMT +2)
