Skip to content

Repository files navigation

Encrypt Selection

Select any text in a note, give it a password, and it becomes unreadable on disk. Everything else in the note stays exactly as it was.

What it does

Most encryption tools for Obsidian work on whole notes or whole vaults. This one works on a selection: a password, a recovery code, a paragraph of a journal entry, one line of an otherwise ordinary note. You highlight it, run a command, type a password, and the highlighted text is replaced by an opaque token. The rest of the note is untouched and still searchable, linkable and editable.

Reading it back does not require putting the plaintext into the note again. The primary read command, peek, decrypts into a modal and leaves the file alone, so the plaintext never reaches disk just because you wanted to look at it.

Encryption is AES-256-GCM through the browser's own Web Crypto API. There are no external binaries, no key files, no GPG setup and no runtime dependencies, and it works on mobile as well as desktop.

An Obsidian note in editing view. Under the heading "Single-line test", the line "The line below is encrypted by Encrypt Selection!" is followed by an inline code span containing an opaque aes256: token.

Install

From inside Obsidian: Settings → Community plugins → Browse, search for Encrypt Selection, install and enable it.

Manually: download main.js, manifest.json and styles.css from the latest release into <your vault>/.obsidian/plugins/encrypt-selection/, then enable the plugin in Settings → Community plugins.

Quick start

Three things, in about a minute:

  1. Encrypt something. Select a line of text. Open the command palette (Ctrl/Cmd + P) and run Encrypt. Type a password, then type it again to confirm. The selected text is replaced by a token.
  2. Read it back. Put the cursor anywhere inside that token — no need to select it precisely — and run Decrypt (peek, read-only). The plaintext appears in a modal. The note on disk is not changed.
  3. Lock up. Your password is held in memory so you are not asked for it again and again. A status bar item shows when that is the case. Click it, or run Forget the cached password, to clear it. It is cleared automatically when Obsidian closes.

Use Decrypt in place only when you actually need to edit the text, and re-encrypt when you are done. That command writes the plaintext back into the file.

What it looks like in your note

Before:

Router admin: 192.168.1.1 — password hunter2-correct-horse

After encrypting the password with a single-line selection, you get an inline code span:

Router admin: 192.168.1.1 — password `aes256:rlIBAQEAAAGGoAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDE=`

A multi-line selection becomes a fenced block instead:

```aes256
rlIBAQEAAAGGoAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwd
Hh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNE
```

In reading view neither form shows up as a wall of base64. Both render as a lock pill you can click to decrypt; the text is revealed in that view only and the file is never rewritten. Click again to hide it.

The same note in reading view. Where the encrypted block was, a single wide pill reads "Encrypted — click to decrypt".

Locked: a fenced block renders as one pill, with no base64 in sight.

The pill clicked open, showing the decrypted text "Dear diary, today I selected several lines at once and they all went into one encrypted block." with a "hide" control beneath it.

Unlocked: the text is revealed in this view only. The file on disk is unchanged, and "hide" locks it again.

Commands

All four live in the command palette.

Command What it does Touches the file?
Encrypt Replaces the highlighted text with an encrypted token Yes
Decrypt (peek, read-only) Shows the plaintext in a modal No
Decrypt in place Writes the plaintext back into the note so you can edit it Yes
Forget the cached password Clears the password held in memory No

Both decrypt commands find the token from your cursor position, so you can simply click into it. If decryption fails — wrong password, altered text — you get a notice and the note is left exactly as it was.

Settings

Setting Default Notes
Token style Auto Auto uses an inline span for single-line selections and a fenced block for multi-line ones. Can be forced either way.
Key derivation iterations 600,000 PBKDF2 rounds, between 100,000 and 5,000,000. Stored in each payload, so changing it never breaks existing text.
Confirm password when encrypting On Asks twice. A typo here is unrecoverable. Leave it on.
Offer a password hint Off Adds an optional hint field. The hint is stored unencrypted next to the ciphertext — it has to be readable before you type the password.
Remember password for this session On Keeps the password in memory until Obsidian closes. Never written to disk.
Show lock widget in reading view On Renders tokens as a clickable lock pill instead of base64.

Security

This plugin exists for one threat: someone gets your vault files — a stolen laptop, a synced folder on a compromised machine, a backup someone else can read. It is not designed to hide anything from software already running as you on an unlocked machine.

Read this before you rely on it

Encrypting the text is not the whole job. Several Obsidian features keep copies of your notes from before you encrypted them, and encrypting today does nothing about yesterday's copy.

  • File recovery (a core plugin, on by default) stores periodic plaintext snapshots of every note in a local database inside your vault. Anyone with your vault can read them. Disable it, or clear its history, if this plugin is protecting anything that matters.
  • Obsidian Sync retains pre-encryption versions in cloud version history. Encrypting a paragraph does not remove the earlier version from that history.
  • Undo history holds the plaintext for the rest of the session. Pressing undo after encrypting brings it straight back — which also means the plaintext is in memory until you close the note.
  • The search index may still contain pre-encryption content until it is rebuilt.
  • Backups — File History, Time Machine, OneDrive, Dropbox, git — contain the pre-encryption file. If the note is in a git repository, the plaintext is in its history permanently.

The plugin repeats the first two warnings in its settings tab, since they are the ones most likely to catch you out.

Also true, and unavoidable:

  • Note titles, file names, links and any text you did not select stay readable. Encrypting the body of a note called Bank logins.md does not hide much.
  • JavaScript cannot reliably wipe memory. Your password stays in RAM until Obsidian closes or you run the lock command, and even then the runtime may keep copies you cannot reach.
  • Your password is the whole security of this. Someone holding the file can guess offline, as fast as their hardware allows. A short or reused password falls quickly, no matter how good the cipher is.

What the plugin does not do

Verifiable in the source, and in the shipped main.js:

  • No network access of any kind. No fetch, no XMLHttpRequest, no WebSocket, no remote assets. The built bundle contains no URLs. Your passwords and note contents never leave the machine, because there is nothing in the code that could send them.
  • No telemetry, analytics or crash reporting. Nothing is counted, logged or phoned home.
  • No file access. The plugin only reads and writes the text of the note you are editing, through Obsidian's editor API. It never touches files itself, inside or outside the vault.
  • No dynamic code loading. No eval, no new Function, no downloaded scripts. It cannot change its own behaviour after installation.
  • No dependencies. main.js is built from this repository's source and requires nothing but obsidian itself, so there is no third-party code in the trust surface and no supply chain to compromise.

For a tool whose entire job is keeping secrets, "you can check this yourself" matters more than any promise. Everything above is a one-line grep against the source or the released bundle.

There is no password recovery

If you forget the password, the text is gone. Permanently. There is no backdoor, no reset, no support address that can help, and no amount of remembering nearly the right password will do. That is exactly what makes it work.

The confirm-password prompt is on by default for this reason. Leave it on, and keep the password somewhere that is not the note you just encrypted.

How the encryption works

  • AES-256-GCM, via the Web Crypto API. GCM is authenticated: a wrong password or a single altered byte fails loudly instead of producing plausible garbage.
  • PBKDF2-HMAC-SHA256 for key derivation, 600,000 iterations by default — the current OWASP recommendation. That is roughly 100 ms per unlock on a modern desktop, so you can raise it substantially before it becomes noticeable. The iteration count is recorded in every payload, so changing the setting never orphans existing text.
  • A fresh random 16-byte salt and 12-byte IV for every encryption, never reused.
  • The payload header is passed to GCM as additional authenticated data, so the version, iteration count and hint cannot be altered without failing the tag check.
  • Zero runtime dependencies. Nothing but the crypto that ships with the platform. The password is cached in memory only and is never handed to saveData().

Development

npm install
npm test          # crypto round-trip, tamper detection, token parsing
npm run typecheck
npm run build     # production bundle
npm run dev       # watch mode

npm run dev builds and installs the plugin into the throwaway test-vault/ inside this repository (test-vault/.obsidian/plugins/encrypt-selection/). Open that folder as a vault in Obsidian — File → Open vault → Open folder as vault — and enable the plugin there.

You can point the install elsewhere with the OBSIDIAN_PLUGIN_DIR environment variable, and the build will warn you loudly when you do. Never point it at a vault you care about: dev builds reload on every keystroke, and this plugin writes ciphertext into notes.

Layout

src/crypto/     AES-GCM + PBKDF2 and the binary payload format    (pure, unit-tested)
src/format/     how tokens are written into and found in markdown (pure, unit-tested)
src/ui/         password modal, peek modal, reading-view pill
src/commands.ts command handlers
src/session.ts  in-memory password and derived-key cache
src/settings.ts settings tab, including the security notice
src/main.ts     plugin entry point

crypto/ and format/ never import from obsidian, which is what lets them be tested in plain Node without an Obsidian runtime.

Payload format

The bytes behind the base64

Each token is the base64 encoding of a self-describing binary struct, big-endian and length-prefixed:

magic 0xAE 0x52 | version u8 | kdfId u8 | cipherId u8 | iterations u32be
| saltLen u8 | salt | ivLen u8 | iv | hintLen u16be | hint (UTF-8)
| ciphertext || GCM tag (16 bytes)

Everything before the ciphertext is the header, and the whole header is passed to AES-GCM as additional authenticated data — so it is authenticated but not encrypted. Overhead is about 57 bytes, or roughly 76 base64 characters.

The hint, if you enable it, is stored in the clear by necessity: it has to be readable before the password is known. Do not put anything in a hint that you would not write in the note itself.

In markdown, the base64 appears in one of two forms, both recognised by the same parser (base64 never contains a backtick, so neither form can break its own delimiters):

  • inline: `aes256:<base64>`
  • block: a fenced code block tagged aes256, with the base64 wrapped at 64 columns

Licence

MIT. See LICENSE.

About

Obsidian plugin for encrypting selected text with AES-256 encryption

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages