GSoC 2026 Proposal Draft – Idea 7: Selective local protection for sensitive notes – Rohit Kumar

Links

1. Introduction

My name is Rohit Kumar. I am based in Punjab, India, and I mainly work with JavaScript, TypeScript, React, and Electron. I started contributing to Joplin before the GSoC application period to understand the codebase, development workflow, and review expectations. My earlier closed PRs helped me improve how I scope changes and respond to feedback.

My strongest contribution so far is PR #14599, which fixes duplicate tags caused by Unicode normalization differences. That work involved shared library code, desktop/mobile UI behavior, tests, and several rounds of review and refinement. It helped me understand how Joplin handles cross-platform behavior and how important it is to keep changes narrow, correct, and reviewable.

I would prefer Joplin over other organisations because I have already invested time in understanding its codebase and contribution workflow through real contributions and review cycles, and I want to see that work through.

2. Project Summary

Joplin already supports end-to-end encryption for synced data, but that does not protect selected notes once a local profile is open. Some users may still want a second layer of local protection for especially sensitive notes.

My proposal is to add selective local protection for individual notes. In the first phase, protected note bodies would be encrypted locally, require explicit unlock before viewing or editing, and have clearly defined behavior for previews, search, and history.

I am intentionally keeping notebook-level protection outside the guaranteed scope and treating it as a stretch goal. I think a finished and secure note-level feature is more realistic than a broader but incomplete solution.

Expected outcome:

  • users can mark a note as protected
  • protected note bodies are encrypted in local storage
  • users can unlock, view, edit, and re-lock protected notes
  • locked notes behave safely in previews and search
  • the feature works on both desktop and mobile

3. Technical Approach

First-phase product decisions

To keep the project realistic, I would make these decisions for the first phase:

  • one local-protection password for all protected notes, not a separate password per note
  • protect note body content in the first phase
  • keep note title visible in the first phase
  • exclude locked note bodies from search indexing
  • do not include attachments/resources in the first phase
  • keep notebook-level protection as stretch only

I prefer one local-protection password because it simplifies key management, is more practical for users, and is easier to implement safely than a separate password flow for every note.

Key management

This is the most important part of the feature.

My planned first-phase approach is:

  • the user sets one local-protection password
  • a key is derived from that password using the same family of crypto primitives already used in Joplin's newer encryption code
  • based on the current codebase, that means reusing the existing PBKDF2 + AES-256-GCM path where practical, rather than introducing a separate custom crypto stack
  • Joplin's current newer encryption path already uses PBKDF2 with 220000 iterations in the native crypto path, so that is the direction I would align with unless mentor feedback suggests a different integration
  • the raw password is never stored
  • the derived key is kept only for the active unlock session
  • in the first phase, I would define the unlock session narrowly: a note is unlocked only while it is actively open for viewing/editing, and it is re-locked on manual lock, note switch, or app restart

If the password is forgotten, protected note content should be treated as unrecoverable in the first phase. I would not promise a recovery mechanism unless that is explicitly designed and approved.

Existing Joplin architecture to build on

Joplin already has relevant encryption pieces:

  • packages/lib/services/e2ee/EncryptionService.ts
  • packages/lib/services/e2ee/crypto.ts
  • packages/lib/models/BaseItem.ts for sync-time item encryption
  • existing password/encryption configuration flows on desktop and mobile

However, current E2EE is mainly for sync-time encryption. This project is different: it is about protecting selected notes locally even when a profile is already open.

The code paths I expect to work with are:

  • packages/lib/models/Note.ts
  • packages/lib/services/e2ee/EncryptionService.ts
  • packages/lib/services/e2ee/crypto.ts
  • packages/lib/services/search/SearchEngine.ts
  • note editor/viewer flows on desktop and mobile
  • revision/history-related note paths

Integration point

My current plan is to keep the protection logic close to the note model/save flow rather than adding a separate parallel storage path. In practice, the protected note body would be encrypted before the protected content is persisted, and decrypted only when the note is explicitly unlocked for viewing or editing. I would finalize the exact integration point after reviewing Note.ts save/load paths in more detail with mentors, but the goal is to keep it close to existing note lifecycle code rather than introducing a disconnected subsystem.

Revision/history behavior

This is the part I thought about most carefully.

My proposed first-phase behavior is:

  • once a note is converted into a protected note, new revisions for that note should not store plaintext body content
  • for existing plaintext revisions, the safer first-phase approach is to disable or purge revision history for that protected note when protection is first enabled

I think this stricter approach is better than claiming the note is protected while leaving readable historical copies behind.

Search and preview behavior

This also needs a clear rule.

My proposed first-phase behavior is:

  • when a note becomes protected, any searchable body representation already stored for that note is removed from indexing
  • locked note bodies are not searchable
  • locked notes show placeholder preview content
  • the note title remains visible in the first phase for usability and lower implementation risk

Cross-device behavior

My intended first-phase design is that the protection state is part of the note and syncs, while the unlock state remains local to each device.

In practice:

  • the protected note body would sync in its protected form across devices
  • a device that downloads the note sees it in locked state
  • each device needs the same local-protection password to unlock protected content
  • edits made while unlocked are re-encrypted before sync
  • removing protection on one device syncs as a normal note state change, making the note readable everywhere again

If a device does not have the local-protection password configured, the note stays locked there until the password is entered. There is no silent plaintext fallback.

I do not plan to change the sync protocol itself. The existing sync path already handles encrypted item content via BaseItem.serializeForSync(), and the intent is to align with that pattern rather than introduce a parallel one.

Usability trade-offs

These are conscious decisions, not oversights.

Title visible vs hidden — keeping the title visible in the first phase makes note list navigation practical and reduces implementation complexity. The note body is the main protected payload.

Unlock session scope — re-locking on note switch, manual lock, or app restart is a deliberate middle ground. It avoids aggressive timeout friction while still being stronger than manual-only locking. Unlock state is always local and never synced.

Single password — one local-protection password for all protected notes is the only practical first-phase answer. It keeps key management simple and avoids per-note password fatigue.

Search and preview — locked note bodies are not searchable and previews show placeholder content. This is consistent with how Joplin's existing search already handles encrypted notes: SearchEngine.ts explicitly excludes notes with encryption_applied = 1 from indexing.

Password change behavior

A password change would require re-encrypting all currently protected note bodies with a new derived key. I do not plan to make password-change UX the main focus of the first implementation phase, but I would design the protected-note storage format so that this re-encryption flow is possible as a follow-up without redesigning the feature. If the core note-level feature is stable early enough, password change handling could be explored as a stretch improvement.

Security considerations

The goal of the first phase is to protect note content in local storage and normal app flows without making unrealistic security claims.

The proposal explicitly aims to address:

  • local storage encryption of protected note bodies
  • removing plaintext note body exposure from previews while locked
  • removing locked note bodies from indexing/search
  • defining a safe rule for revisions/history
  • carefully handling unlock/relock transitions

What I am not claiming is that the first phase solves every OS-level or runtime-level plaintext exposure case, such as all clipboard history or every editor-internal undo edge case. Those matter, but I do not want to overstate the guarantees of a first release.

Rough UI flow

A user would initially protect a note through a note-level action such as a toolbar or menu command like Protect note, which would prompt for the local-protection password if it is not already configured.

Note list:

+-----------------------------+
| Tax notes        [Locked]   |
| This note is protected      |
+-----------------------------+

Locked note screen:

+----------------------------------+
| Title: Tax notes                 |
| -------------------------------- |
| This note is protected           |
| [Unlock note]                    |
+----------------------------------+

Unlock modal:

+----------------------------------+
| Unlock protected note            |
| Password: [..................]   |
| [Cancel]           [Unlock]      |
+----------------------------------+

Unlocked note screen:

+----------------------------------+
| Title: Tax notes                 |
| Body shown normally              |
| [Lock note]                      |
+----------------------------------+

4. Implementation Plan

Weeks 1-2

  • go through the note, encryption, search, and revision code paths carefully
  • align with mentors on the hard first-phase decisions: key management, unlock session rules, search behavior, revision/history behavior, sync representation
  • write a short technical design note before touching any code

Weeks 3-4

  • add protected-note state and note-level metadata
  • implement encrypted body storage and the decrypt/re-encrypt flow
  • write model-level tests covering protect, unlock, edit, and re-lock

Weeks 5-6

  • wire up protected-note behavior with search/indexing
  • implement the agreed revision/history rule
  • add regression tests for indexing and history behavior

Weeks 7-8

  • build the full desktop flow: lock action, unlock prompt, locked placeholder, edit and re-save

Weeks 9-10

  • build the equivalent flow on mobile
  • adjust for mobile-specific editor and viewer differences
  • test lock/unlock/edit on mobile

Weeks 11-12

  • clean up error handling and UX rough edges
  • write documentation
  • stretch work only if the core feature is stable

Mobile is the highest schedule risk given the editor differences from desktop. If it runs over, I would flag it early and make sure the shared model layer and at least one platform are solid before going further.

5. Deliverables

Required deliverables

  • per-note local protection model/state
  • encrypted local storage for protected note bodies
  • single local-protection password flow
  • desktop UI for locking, unlocking, viewing, and editing protected notes
  • mobile UI for locking, unlocking, viewing, and editing protected notes
  • defined behavior for search, previews, and revisions for protected notes
  • automated tests
  • user and developer documentation

Optional / stretch deliverables

  • notebook-level protection built on top of the note-level design
  • improved unlock-session handling
  • additional UX refinements

Out of scope for the first phase

  • changing the sync protocol
  • redesigning Joplin's sync-time E2EE
  • attachment/resource encryption
  • full-text search inside locked note bodies
  • broad encryption overhaul across all note-related data

6. Availability

I am based in Punjab, India (IST, UTC+5:30).

I have college exams before the GSoC coding period begins, so my availability during the application period is lower. Once the coding period starts, I expect to contribute at least 20-25 hours per week consistently, with availability increasing further as my semester workload reduces.

I am usually available after 13:00 IST on weekdays. I am comfortable with regular async communication and can provide weekly progress updates.

I would especially appreciate feedback on whether the first-phase scope is realistic, particularly around key management, revision/history handling, and sync behavior.

Thanks for your draft proposal. It would be useful to clarify some edge cases around sync (especially across devices) and think a bit more about usability trade-offs for locking/unlocking notes.

Thanks — I've updated the draft. The main additions are a cross-device behavior section (protection state syncs, unlock state stays local, no silent plaintext fallback on other devices) and an explicit usability trade-offs section covering session scope, single password, and search behavior. Happy to adjust further based on your feedback.