0xFAFE30 downloadsEncrypt selected files and lock plaintext after inactivity, tab closure, or on demand.
Keep private notes and images encrypted inside your Obsidian vault, and unlock them only when you need them.
Vault in Vault helps you:
.age files..ageconfig policy.Vault in Vault protects file contents. File names and folder structure remain visible.
[!WARNING] Vault in Vault writes plaintext to disk while a file is unlocked. Keep backups and read the security model before using it with important data. This project has not received an independent security audit.
English · 简体中文 · 繁體中文 · 日本語 · 한국어
Vault in Vault is currently available on desktop only. You can also view its Obsidian Community listing.
Encrypted files are marked AGE in the file explorer. Their names and folder structure remain available, while their contents are unreadable without the password.

Select Decrypt and open when you want to read or edit the file.

Enter the password. You can optionally remember it in memory for the current Obsidian session; it is not saved to disk.

After decryption, the note opens in Obsidian's normal editor. Markdown features, image previews, links, search, and compatible plugins continue to work while the file is unlocked.
When the last protected plaintext tab closes, choose whether to encrypt that file, encrypt every matching plaintext file, or leave the files as they are. Closing one protected tab while another remains open does not interrupt you.

The settings page shows the file extensions covered by vault-wide locking. Settings are visible by default, but changing them requires password verification to prevent accidental edits.

Add vault-relative paths to exclude in .ageconfig when a public folder, template, or individual file should never be included in encryption or password-verification scans. A folder entry excludes its entire subtree. The settings page shows the active exclusions as read-only when the shared policy is in use.

Session security offers two mutually exclusive choices. Password auto-clear only removes the cached password after a fixed time. Auto-lock after Vault inactivity waits for a period without activity, saves open Markdown editors, encrypts matching plaintext files, closes successfully protected tabs, and clears the password.

The Ribbon icon shows the configured mode separately from password status:
![]()
.age files from the file explorer..md, .avif, .bmp, .gif, .jpeg, .jpg, .png, .svg, and .webp..age.Opening notes/private.md.age and selecting Decrypt and open performs this transition:
notes/private.md.age
-> decrypt and verify notes/private.md
-> remove notes/private.md.age
-> open notes/private.md in the native editor
When the last open protected plaintext tab closes, Vault in Vault offers three choices and a collapsed list of all matching plaintext files. If .ageconfig excludes paths, a second collapsed list shows which configured files and folders will be skipped:
The tab closes first, allowing Obsidian to finish saving it. Encryption then runs while the app remains open. If the password prompt is cancelled, a file changes during encryption, or verification fails, the plaintext source is retained.
Choosing Encrypt all or running Encrypt and lock vault now deliberately forgets the cached session password after encryption. Opening another encrypted file then asks for the password again. Encrypting only the closed file keeps the current session password cached.
The plugin does not attempt interactive encryption from an application quit hook. Obsidian does not reliably wait for that asynchronous work. Before quitting, close protected tabs or run Encrypt and lock vault now.
Images use the same in-place workflow:
photo.png.age and select Decrypt and open image for the native image preview.photo.png, the plugin can find and decrypt photo.png.age automatically.Image links in Markdown retain their ordinary names and do not need an .age suffix.
.age files are used to verify the supplied password before a vault-wide encryption operation..age file exists, the password must be entered twice.data.json.The settings page offers two mutually exclusive timers. Both are off by default:
The ribbon uses a key with a clock badge for password auto-clear and a lock with a circular-arrow badge for automatic idle lock. With both timers off it shows the ordinary lock. The badge identifies the configured mode; the separate red, green, or yellow bar identifies password availability and an approaching deadline. The settings page uses the same icons.
Automatic idle lock requires the password to remain cached. While that mode is enabled, password prompts keep Remember until automatic lock enabled. A restart never restores the password, so automatic lock is unarmed until a password is entered again.
The timer measures activity visible to the plugin in this Vault's Obsidian windows, including popouts. It is not operating-system idle detection. Switching to another app continues the countdown. Background sync and unrelated file modification times do not reset it. If the computer sleeps or Obsidian is suspended, the deadline is checked as soon as the app resumes.
Open Settings -> Vault in Vault to inspect the protected extensions, excluded paths, embedded-image behavior, and session security timers. Editing the protection policy is locked until an existing vault password is verified. Timer controls remain available so a cached password can always be constrained. The UI lock prevents accidental policy changes; it is not a security boundary.
Settings are stored per vault in:
<vault>/.obsidian/plugins/vault-in-vault/data.json
The actual configuration directory may be customized by Obsidian. Vault in Vault obtains it from the Obsidian API rather than assuming .obsidian internally.
.ageconfig policyTo share one policy with the companion Go CLI, create .ageconfig in the vault root:
{
"extensions": [".md", ".png", ".jpg"],
"exclude": ["Public", "Templates/daily.md", "attachments/shared"]
}
exclude entries are vault-relative exact file paths or directory paths. A directory excludes its entire subtree. Paths are case-sensitive and do not support globs; absolute paths and .. are rejected. Excluding a plaintext name also excludes its corresponding .age file from CLI decrypt and password-change scans.
extensions is optional. A policy containing only exclusions uses the built-in Markdown and common-image defaults:
{
"exclude": ["Public", "Templates"]
}
An explicitly supplied extensions array must not be empty.
When .ageconfig exists, it takes precedence over the extension and exclusion lists in data.json. The plugin displays those shared fields as read-only. The settings page can open the file in the system default editor or reveal it in Finder/the desktop file manager; use Reload .ageconfig after saving. data.json remains the fallback when .ageconfig is absent. An invalid .ageconfig blocks encryption instead of silently falling back to a broader policy. Neither file stores the password.
Vault in Vault protects selected files at rest, subject to these boundaries:
The plugin has no telemetry, advertisements, account requirement, network service, or access outside the current vault. Cryptographic code is bundled into main.js; normal use is offline.
To install a local development build, use Node.js 20 or newer:
npm ci
npm run check
npm run install:vault -- "/absolute/path/to/your/vault"
The last command copies main.js, manifest.json, and styles.css to:
<vault>/.obsidian/plugins/vault-in-vault/
Alternatively, copy those three files manually. Restart Obsidian or reload the app, then enable Vault in Vault under Settings -> Community plugins.
If you installed an earlier development build under fileencrypt-age-viewer, disable that build before enabling vault-in-vault. The new ID is intentionally treated as a separate plugin. Copy the old data.json into the new plugin folder only if you want to preserve its extension settings; it contains no password.
npm ci
npm run dev # watch and rebuild main.js
npm test # run the test suite
npm run check # test, type-check, and create a production bundle
Tests cover age round trips, a Go-generated age fixture, wrong passwords, Unicode Markdown, randomized ciphertext, shared extension/exclusion configuration, configuration-directory exclusion, image references, and tab-close tracking.
npm version patch, npm version minor, or npm version major. This synchronizes package.json, manifest.json, and versions.json.npm run check and test the build in a disposable vault.v prefix.main.js, manifest.json, and styles.css appear as separate downloadable assets.The workflow is the normal Create a release step; there is no need to create a second release or upload the files manually. Obsidian downloads these assets from the published release whose tag matches manifest.json.version.
Vault in Vault 0.8.0 and later requires Obsidian 1.8.7 or newer so it can follow Obsidian's selected interface language.
Vault in Vault uses age-encryption with an age scrypt work factor of 14. Files are compatible with standard passphrase-encrypted age files. See THIRD_PARTY_NOTICES.md for bundled dependency notices.
Vault in Vault is desktop-only because opening and revealing .ageconfig uses Electron and desktop filesystem APIs. Mobile is not currently supported.
If you're also using Obsidian calendar plugin, you may take a look at the updated version which support .md.age extension.