Obsidian plugin for encrypting sensitive text directly inside notes with interoperable age encryption.

~/.config/age/keys.txt) and passphrases before relying on this plugin.secure block containing age ASCII armor.sec:v1: inline tokens.secure/age fences when the cursor leaves the block.secure-properties keys readable while encrypting every scalar value as sec:v1:..age sidecar; preview or save decrypted copies in-app, or age -d file.ext.age on the CLI.age CLI.```secure
-----BEGIN AGE ENCRYPTED FILE-----
...
-----END AGE ENCRYPTED FILE-----
```
If ciphertext ever contains a triple-backtick sequence, Secure Inline emits a four-backtick fence. Decryption also recognizes age fenced blocks for compatibility.
sec:v1:<unpadded-base64url-age-ciphertext>
Inline tokens are intentionally versioned. Legacy unversioned sec: tokens are rejected as invalid.
```secure-properties
username: arie
api_key: sec:v1:<unpadded-base64url-age-ciphertext>
password: sec:v1:<unpadded-base64url-age-ciphertext>
```
secure-properties keeps keys readable and encrypts every non-empty scalar value. Both key: value and dotenv-style KEY=value lines are supported; comments, blank lines, nested YAML, lists, block scalars, and other structured values are left unchanged.
Create a native age key file outside the vault:
mkdir -p ~/.config/age
age-keygen -o ~/.config/age/keys.txt
The plugin reads identities and public recipients from ~/.config/age/keys.txt by default. It understands:
AGE-SECRET-KEY-1... identities# public key: age1... commentsage1... recipientsUnsupported AGE-PLUGIN-* identities are skipped and reported as warnings. Keep private key files outside the synced vault.
In plugin settings, use Generate under Device recipient. Secure Inline generates an age identity, stores the private identity in Obsidian SecretStorage, and displays only the public age1... recipient.
Copy that public recipient into Extra recipients on other devices, or into another age recipient set, so those devices can decrypt content encrypted for this device.
data.json stores only the SecretStorage secret name. It never stores the private identity.
In Device identity settings, Export asks for a passphrase and copies an age-armored AGE-SECRET-KEY-1… to the clipboard. On the other device, Import accepts the decrypted identity (or a pasted raw AGE-SECRET-KEY-1…). The exported armor contains the private key — treat it like the secret it protects.
Add one age1... recipient per line in settings. These recipients are merged with public recipients found in keys.txt and the generated device identity recipient.
sec:v1: tokens.secure armor blocks.secure or age fence.secure-properties fence as key: value lines.sec:v1: values, and blocks already containing -----BEGIN AGE ENCRYPTED FILE----- are skipped.photo.png → photo.png.age)..age files open in a dedicated view: Unlock & preview renders images and text in place; any file can be saved decrypted via Save decrypted copy..age file and choose Decrypt to source file to write the original back (name collisions get a -decrypted suffix).age -d -i keys.txt file.png.age recovers the original outside Obsidian. Filenames stay cleartext.secure/age block to reveal it.sec:v1: token or secure-property value to reveal it.secure-properties renders as a key/value table; encrypted values reveal in place and expose per-value Copy and Relock controls.sec:v1:<plaintext> without changing the file.A fenced block's contents are standard age ASCII armor. Extract the armor to a file and decrypt with:
age -d -i ~/.config/age/keys.txt secret.age
Inline tokens are raw age ciphertext encoded as unpadded base64url, not armor. Decode the base64url payload before passing it to age -d if you need CLI access to an inline token.
From this repository:
npm install
npm run install:local -- "/path/to/vault"
The installer builds main.js and copies main.js, manifest.json, and styles.css into /path/to/vault/.obsidian/plugins/secure-inline/. Restart Obsidian or reload plugins, then enable Secure Inline (age) in community plugin settings.
For development symlinks instead of copies:
npm run install:local -- "/path/to/vault" --link
For Android, run the installer against the synced vault directory before opening it in Obsidian mobile, or use a shell with Node.js on the device.
~/.config/age/keys.txt outside the vault.secure/age/secure-properties fence can remain in the note until the cursor leaves the block or the sweep command runs.secure-properties intentionally stores keys in plaintext; values are stored as ciphertext after encryption..age sidecar filenames stay cleartext; only contents are encrypted.npm test
npm run build
npx tsc --noEmit
The esbuild bundle keeps obsidian, electron, @codemirror/*, and Node builtins external. Node filesystem access is dynamically required only behind the desktop platform branch.