Encrypted Notes (ep-plugin)
unlistedby ep-plugin
Create notes that are encrypted on disk with a passphrase (AES-GCM 256). Open them inside Obsidian with the same passphrase.
ep-plugin — Encrypted Notes
把 Obsidian 的笔记用 AES-GCM 256 + PBKDF2 加密后存盘。任何不安装本插件的软件打开文件只会看到 base64 密文;在 Obsidian 里输入密码即可解密、正常编辑、保存时自动重新加密。
📥 Download latest release · Report bug · Request feature

目录
特性
- 🔐 强加密:AES-GCM 256 位,PBKDF2 / SHA-256 / 250 000 次派生密钥
- 📁 磁盘上始终是密文:除了本插件,没有任何软件能读出原文
- 🔄 透明的保存流程:保存时自动加密,编辑器里仍是明文
- 🚀 零摩擦切 tab:已解锁的笔记,切来切去永远显示明文,不会变密文
- 🔑 改密码:随时改,文件重新加密写盘
- 🔓 永久解密:一键把加密文件转回明文(一次性、不可逆)
- 🖱️ 右键菜单 + 工具栏按钮 + 命令面板:三种入口
- 🤖 AI Agent 集成:附带的
epCLI 共用同一份加密代码,AI agent 可以读 / 写加密笔记
安装
方式 A:从 GitHub Release 直接装(推荐给普通用户)
每个 release 提供两个 zip,按需取用:
| 文件 | 内容 | 给谁 |
|---|---|---|
ep-plugin.zip | main.js + manifest.json + styles.css | Obsidian 用户 |
ep-cli.zip | ep (含 shebang,10 KB) | AI agent / CLI 用户 |
Obsidian 端:
- 去 Releases 下载最新版的
ep-plugin.zip - 在你的 vault 里建目录:
<vault>/.obsidian/plugins/ep-plugin/ - 把
ep-plugin.zip里的三个文件解压进去 - Obsidian → Settings → Community plugins → Installed plugins → 打开 Restricted mode(如果开着)→ 找到 Encrypted Notes (ep-plugin) → Enable
CLI 端(让 AI agent 读写加密笔记):
- 下载
ep-cli.zip - 解压得到单个
ep文件 chmod +x ep && mv ep /usr/local/bin/(或任何$PATH目录)export EP_PASSPHRASE='你的密码'
方式 B:从源码构建(开发者)
git clone https://github.com/yccb-ly/ep-plugin.git
cd ep-plugin
npm install
npm run build # 生产构建,产物在 main.js
# 然后把整个目录链接或复制到 <vault>/.obsidian/plugins/ep-plugin/
# 想边改边自动重新构建?
npm run dev
快速上手
第 1 步:创建一个加密笔记
两种方式:
- 点击左侧 ribbon 的 🔒 图标
- 命令面板搜 "Create encrypted note"
弹出对话框:

填文件名 + 两次密码 → Create。
ASCII mockup:
┌────────────────────────────────────────────┐
│ Create encrypted note │
│ ───────────────────────────────────── │
│ File name (with or without .md) │
│ ┌────────────────────────────────────┐ │
│ │ my-secret.md │ │
│ └────────────────────────────────────┘ │
│ Passphrase │
│ ┌────────────────────────────────────┐ │
│ │ •••••••• │ │
│ └────────────────────────────────────┘ │
│ Confirm passphrase │
│ ┌────────────────────────────────────┐ │
│ │ •••••••• │ │
│ └────────────────────────────────────┘ │
│ [ Cancel ] [ Create ]│
└────────────────────────────────────────────┘
新文件落盘后会自动在当前 pane 打开,并显示解密后的内容(默认模板):

第 2 步:写入内容 + Ctrl+S
正常写文字,正常保存。插件会在 Vault.modify 这一步拦截,自动加密再写盘。编辑器里始终是明文,磁盘上始终是密文。
第 3 步:用其它软件打开验证
cat 一下那个 .md:
$ cat /path/to/vault/my-secret.md
RVBNA...(一坨 base64)...AAAA==
任何不知道密码的人 / 软件都只能看到这串密文。
第 4 步:关掉再打开 → 自动解密
关掉这个 tab → 在文件管理器重新点开同一个文件 → 不需要重新输密码(密码还在内存里),Obsidian 会自动调用 vault.read,被插件拦截后返回明文,渲染在编辑器里。
完全退出 Obsidian 再开 → 弹密码框 → 输入正确密码 → 解密。
操作截图
⚠️ 截图占位:请把实际截图放到
docs/screenshots/下,文件名对应下面的引用。
| # | 场景 | 文件 |
|---|---|---|
| 01 | 创建加密笔记的弹窗 | docs/screenshots/01-create.png |
| 02 | 创建后的编辑器(明文) | docs/screenshots/02-after-create.png |
| 03 | 重新打开时的密码弹窗 | docs/screenshots/03-unlock.png |
| 04 | 工具栏上的 🔒 按钮 | docs/screenshots/04-toolbar.png |
| 05 | 编辑器内右键菜单 | docs/screenshots/05-editor-menu.png |
| 06 | 文件管理器右键菜单 | docs/screenshots/06-file-menu.png |
| 07 | 永久解密的确认弹窗 | docs/screenshots/07-decrypt-confirm.png |
| 08 | 加密文件用其它软件打开 | docs/screenshots/08-other-software.png |
ASCII mockup:编辑器内右键菜单(已解锁状态)
┌──────────────────────────────────────┐
│ ───────────────────────────────── │
│ 🔒 Lock note (ep-plugin) │
│ 🔑 Change passphrase (ep-plugin) │
│ 📝 Decrypt → save as plaintext │ ← ep-plugin
│ ───────────────────────────────── │
│ Cut │
│ Copy │
│ Paste │
│ ... │
└──────────────────────────────────────┘
所有命令与菜单
命令面板
| 命令 | 何时可用 |
|---|---|
| Create encrypted note | 总是 |
| Encrypt / Unlock note (current) | 编辑器中(智能判断:明文加密 / 密文解锁) |
| Lock current note (forget passphrase) | 已解锁文件 |
| Change passphrase for current encrypted note | 已解锁文件 |
| Decrypt current note (save as plaintext, one-way) | 已解锁文件 |
右键菜单
文件管理器右键(.md 文件):
- 🔒 Encrypt / Unlock note (ep-plugin) — 点下去智能判断
编辑器内右键:
- 未解锁时:🔒 Encrypt / Unlock note (ep-plugin)
- 已解锁时:
- 🔒 Lock note (ep-plugin)
- 🔑 Change passphrase (ep-plugin)
- 📝 Decrypt → save as plaintext (ep-plugin)
工具栏(编辑器右上角)
打开加密文件时会出现一个锁形按钮:
| 状态 | 图标 | 点击效果 |
|---|---|---|
| 加密 + 未解锁 | 🔒 | 弹密码框 → 解密 |
| 加密 + 已解锁 | 🔓 | 立刻忘掉密钥,编辑器内容变回密文 |
| 明文文件 | — | 不显示按钮 |
AI Agent 集成 (ep CLI)
仓库里附带一个 ep 命令行工具,跟 Obsidian 插件 共用同一份加密代码(src/crypto.ts),所以 AI agent(Claude Code 等)可以读 / 写加密笔记,磁盘上始终是密文。
它能干什么
ep decrypt <note> --out <file> # 解密 → file(agent 接下来 Edit/Write)
ep encrypt <note> --from <file> # file → 加密写回 note(原子写)
ep cat <note> # 解密到 stdout(明文则透传)
ep pipe <note> # 从 stdin 读明文,加密覆盖 note
ep is-encrypted <note> # exit 0 = 加密,1 = 明文,3 = 缺文件
ep info <note> # JSON: { encrypted, version?, sizeBytes }
Exit code 约定
| Code | 含义 | Agent 怎么处理 |
|---|---|---|
| 0 | 成功 | 继续 |
| 2 | 密码错 | 停下来问用户,别瞎试 |
| 3 | 文件不存在 | 先 ls 确认路径 |
| 64 | 用法错 / 缺 EP_PASSPHRASE | 重读 --help |
| 65 | 数据格式错 | 别解密,问用户 |
安装
git clone https://github.com/yccb-ly/ep-plugin.git
cd ep-plugin
npm install
npm run build # 同时产出 main.js (Obsidian) 和 bin/ep.js (CLI)
# 全局安装 ep 命令
npm link # 或:ln -s "$PWD/bin/ep.js" /usr/local/bin/ep
一次性配置密码
把密码写到 shell 启动文件里,让 Claude Code 这类 agent 自动继承。不要用 export EP_PASSPHRASE=… 直接在命令行执行 —— 会进 ~/.zsh_history。
# ~/.zshenv(或 ~/.bashrc)
export EP_PASSPHRASE='你的密码'
chmod 600 ~/.zshenv # 文件含 secret,必须 600
典型 agent 工作流
# 1. 把加密笔记解开到 tmp
ep decrypt "/path/to/vault/secret.md" --out /tmp/ep-xyz.md
# 2. agent 用 Read / Edit / Write 工具改 /tmp/ep-xyz.md(明文)
# 3. 改完重新加密写回原文件
ep encrypt "/path/to/vault/secret.md" --from /tmp/ep-xyz.md
# 4. 销毁 tmp
shred -u /tmp/ep-xyz.md # Linux
rm -P /tmp/ep-xyz.md # macOS
⚠️ 并发注意:如果 Obsidian 那边同一个笔记已经打开并解锁,agent 用
ep pipe/ep encrypt改完后,下次 Obsidian 保存会用缓存的旧明文覆盖。先让用户关掉 Obsidian 那个 tab 再让 agent 改。
更详细的 agent 视角说明(Claude Code 的 ep-notes skill)见 ~/.claude/skills/ep-notes/SKILL.md。
文件格式
┌──────────────────────────────────────────────────────┐
│ 字节偏移 长度 含义 │
├──────────────────────────────────────────────────────┤
│ 0 6 魔数 "EPMARK" │
│ 6 1 格式版本 (0x01) │
│ 7 16 PBKDF2 salt(每文件随机) │
│ 23 12 AES-GCM IV / nonce(每文件随机) │
│ 35 … 密文(WebCrypto 自动追加 16B tag) │
└──────────────────────────────────────────────────────┘
整体 base64 编码成一个字符串,所以 .md 后缀不变,任何文本编辑器都能打开(看到的只是 base64)。
file on disk (UTF-8):
"RVBNQVJLAQDDT9...AAAA=="
↓ base64 decode
bytes: 45 50 4D 41 52 4B 01 73 32 9a ... a3 ff 12
└─EPMARK─┘ │└──── salt ────┘│└ IV ┘└ ct+tag ┘
安全说明
- 密码仅存内存:
Map<path, passphrase>,插件卸载 / Obsidian 关闭即清空。下次再开要重新输。 - 不强求密码强度:插件不限制密码长度或复杂度,但 弱密码 = 弱保护,请用长密码 / 密码管理器生成。
- PBKDF2 250 000 轮:这让暴力破解一个强密码的成本很高(每次猜测 ~250ms)。
- 每文件独立 salt + IV:同一明文 + 同一密码,每次加密结果都不同。
decryptAndSaveAsPlaintext是不可逆的:执行后文件就是普通明文.md了,不会再被这个插件加密。如果想反悔,只能再次手动 Encrypt / Unlock note。
已知边界
- 不防键盘记录器 / 屏幕录像:输入密码时自己注意环境。
- 不防已经 root 的恶意进程:如果有人能读 Obsidian 的内存,密码理论上能拿到。
- 明文只在编辑器里短暂存在:写盘永远是密文,但解密后编辑器内存里有明文,直到文件锁 / 关闭。
开发与构建
项目结构
ep-plugin/
├── manifest.json # 插件元信息(id、版本、minAppVersion…)
├── main.ts # Obsidian 插件源码(UI、命令、读写拦截)
├── src/
│ └── crypto.ts # 加密核心(main.ts 和 bin/ep.ts 都引用这一份)
├── bin/
│ ├── ep.ts # CLI 源码
│ └── ep.js # CLI 产物(构建生成,gitignore)
├── test/
│ └── roundtrip.test.ts # node:test round-trip 测试
├── styles.css # 模态框样式
├── tsconfig.json # TypeScript 配置
├── esbuild.config.mjs # esbuild 打包脚本(多入口:main + bin/ep)
├── package.json
├── versions.json # 插件版本 → Obsidian 最低版本映射
├── LICENSE # MIT
├── docs/ # README 引用的截图
├── scripts/
│ ├── release.mjs # `npm run release` 的发布打包脚本
│ └── zip-writer.mjs # 零依赖 STORE-method zip writer
└── README.md
开发流程
npm install
npm run dev # watch 模式,main.ts / bin/ep.ts / src/crypto.ts 任一改动都自动重建
npm run typecheck # tsc --noEmit
npm test # node:test round-trip(13 个测试)
发布流程
打 release tag 时跑一条命令,产出两个 zip 到 release/:
npm run release # 先 build,再产出 release/ep-plugin.zip + release/ep-cli.zip
| 产物 | 大小(参考) | 用途 |
|---|---|---|
release/ep-plugin.zip | ~36 KB | Obsidian 端安装(直接拖进 .obsidian/plugins/ep-plugin/) |
release/ep-cli.zip | ~11 KB | CLI 端安装(解压 → chmod +x → 放到 $PATH) |
release/ 目录已 gitignore,里面的 zip 不进仓库;走 GitHub Releases 分发。zip writer 是 scripts/zip-writer.mjs,零依赖(用 Node 内置 zlib + 手写 STORE 头),所以发布脚本不引入任何运行时依赖。
如何加新功能
| 想加什么 | 改哪里 |
|---|---|
| 新命令 | onload() 里加 this.addCommand({...}) |
| 新右键菜单项 | file-menu / editor-menu 监听 |
| 新工具栏按钮 | refreshToggleButton() 已经管了大多数情况 |
| 新加密算法 | 只改 src/crypto.ts:encryptContent / decryptContent,同步升级 VERSION 和 isEncryptedFile;插件和 CLI 自动同步 |
| 新弹窗 | 仿 PassphraseModal / ConfirmModal |
| 给 CLI 加新子命令 | bin/ep.ts 里 case "..." 一处,外加 README 里 ## 所有命令与菜单 加一行 |
常见问题
Q: 我忘了密码怎么办? A: 没办法。设计如此。如果想保留数据,先确保你记得至少一个版本的密码;否则这个文件就废了。
Q: 文件能改扩展名 / 后缀吗?
A: 可以,但插件目前只对 .md 后缀生效(这是默认配置,Settings 里可改 "Default file extension")。扩展名变了,isEncryptedFile 仍然能识别(靠魔数 EPMARK),只是插件不会自动触发解密。
Q: 我把 vault 同步到云盘 / Git,安全吗? A: 文件在云上 / Git 里就是密文(除非你执行过 Decrypt → save as plaintext)。只要密码不泄漏,云端偷走也解不出来。
Q: 跟其它"加密笔记"插件有什么不一样? A: 大多数同类插件是把内容存到一个外部 vault / 数据库,不修改 markdown 源文件。本插件是 真正的 .md 文件本身加密,磁盘上是 base64 密文,所以哪怕用 Obsidian 之外的软件看,看到的也是密文。
Q: 切 tab 会不会突然变密文?
A: 不会。只要 Lock current note 没主动执行过(或者 Obsidian 没退出),同一个 vault 会话内密码一直在内存里,切 tab、切文件再切回来都是明文。
Q: 我想让 AI agent 帮我改加密笔记,怎么搞?
A: 仓库里附带的 ep CLI 就是干这个的。装好后 export EP_PASSPHRASE='...',agent 调 ep decrypt … --out /tmp/x.md → 用 Edit/Write 改 tmp → ep encrypt … --from /tmp/x.md 写回,全程磁盘上是密文。详见 AI Agent 集成 一节。
Star History
如果这个插件帮到你了,欢迎点 ⭐!也欢迎提 Issue / PR。
MIT License · Made with ❤️ for the Obsidian community
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.