VailNote is a simple, open-source note-sharing app designed for maximum privacy. All notes are encrypted using modern encryption technology before being stored, ensuring that no one else can access them. The app is built with a focus on user-friendliness and security.
🔗 See VailNote in action at vailnote.com.
- 🔒 End-to-end encryption
- 🗂️ Self-destructing notes - automatically deleted after viewing
- 🔑 Optional password protection
- ⏰ Configurable expiration times (10 minutes to 30 days)
- 🚫 No tracking or analytics
- 🛡️ Privacy-preserving rate-limiting using Anonymous Rate-Limited Credentials (ARC) (View Implementation)
- 🤖 AI-agent ready - an official CLI (
cli/main.ts) that encrypts/decrypts locally, so agents can store and share API keys and other secrets without ever writing them to disk in plaintext. The CLI'senvcommand resolves VailNote links stored in.envfiles, so secrets can live by reference instead of plaintext. See llms.txt (or the repo's llms-full.txt) for the full agent-facing documentation.
- Framework: Fresh (Deno)
- Runtime: Deno
- Database: FoundationDB (Deno KV)
- Encryption: AES-GCM with PBKDF2 key derivation for content encryption, bcrypt for password storage
- Frontend: Tailwind CSS & Preact
Note
Safety and transparency are our top priorities. VailNote is made to be as secure as possible while still being easy to use and compatible with most clients. I highly encourage you to look into the architecture to ensure your safety!
Take a look at the architecture diagram for a better visual representation.
Every possible step where I think it might be insecure, given the possibility that the network, server, or database has The system has been compromised and is marked with (!).
- Before sending anything to the server, the content will be encrypted.
- First, the password will be hashed with PBKDF2 for security
- The original password (not the hash) will then be used to encrypt the content.
- If no password is provided, the client will generate a random phrase (auth key).
- The client will send the encrypted content, PBKDF2 hashed password, and expiration time to the server.
- If the document is valid, the server will generate a random note ID, hash the PBKDF2 password again using bcrypt for secure storage, and store the note in the database.
- The server will send a successful response containing the new note ID.
- The client will generate a valid link using the note ID and local auth key using the following structure:
https://vailnote.com/[noteId]#auth=[authKey]
- When a note is accessed, the client fetches the encrypted note data from the server.
- The client asks the user for confirmation before viewing (and destroying) the note.
- If an auth key is present in the URL, the client uses it to decrypt the note. If a password is required, the client prompts for it and decrypts locally.
- The client never sends the raw password or auth key to the server—only a deterministic PBKDF2 hash of the password (used for access control on fetch/delete); decryption always happens in the browser.
- After successful decryption, the client requests that the server delete the note.
- If decryption fails, the note remains on the server until a valid decryption attempt is made or it expires.
(None)
- Deno v2.3 or later
-
Clone the repository:
git clone https://github.com/emilkrebs/VailNote.git cd VailNote -
Set up environment variables:
-
Start the development server:
deno task start
-
Open http://localhost:8000 in your browser
DATABASE_URI- Deno KV connection stringARC_SECRET- Secret for ARC rate-limiting
VailNote ships with an official CLI (cli/main.ts) that AI agents (or anyone) can use to create, read,
and delete end-to-end encrypted notes. Content is encrypted locally and decrypted locally - the server only ever stores
ciphertext - so secrets like API keys never touch the disk unencrypted.
Option 1 - npm (recommended, no Deno needed): most AI agents already ship with Node/npm, so one command works everywhere:
npm install -g vailnote-cli
vailnote --helpOption 2 - Global install with Deno (requires Deno 2.3+):
deno install -g -n vailnote --allow-net --allow-env --allow-read https://raw.githubusercontent.com/emilkrebs/VailNote/main/cli/main.ts
vailnote --helpOr install from a local clone: deno task install:cli.
Option 3 - Single binary (no Deno needed): download the prebuilt binary for your platform from the
GitHub Releases page (vailnote-x86_64-unknown-linux-gnu,
vailnote-aarch64-apple-darwin, vailnote-x86_64-pc-windows-msvc, ...). They are built automatically whenever a v*
tag is pushed. To build one yourself: deno task build:cli (produces ./vailnote-cli).
Option 4 - Run without installing: the CLI is dependency-free, so it runs straight from the repo or GitHub:
deno task cli <command> # from a clone
deno run -A https://raw.githubusercontent.com/emilkrebs/VailNote/main/cli/main.ts <command># Create an encrypted note. Content via stdin, so it never appears in argv or shell history.
echo "sk-1234..." | vailnote create
# The command prints a link; the #auth= fragment is the decryption key.
# https://vailnote.com/<noteId>#auth=<authKey>
# Read/decrypt a note - the plaintext goes to stdout.
vailnote read "https://vailnote.com/<noteId>#auth=<authKey>"
# Delete a note (required for --manual-deletion notes).
vailnote delete "<link>"Machine-readable output for agent tooling:
$ echo "sk-1234..." | vailnote create --json
{
"noteId": "a1b2c3d4e5f6",
"authKey": "ExAmPlE_AuTh",
"link": "https://vailnote.com/a1b2c3d4e5f6#auth=ExAmPlE_AuTh",
"expiresIn": "24h"
}Options:
-p, --password <pw>/VAILNOTE_PASSWORD- protect a note with a password. The env var is preferred over argv.-e, --expires-in <opt>-10m,1h,6h,12h,24h(default),3d,7d,30d,90d,180d-m, --manual-deletion- keep the note until it is explicitly deleted (default: self-destructs after first read)-o, --origin <url>- API origin for self-hosted instances (envVAILNOTE_ORIGIN)-j, --json- machine-readable output on stdout-h, --help- full usage
Instead of a plaintext API key, your .env can hold a VailNote link. The real key never touches disk - each resolution
fetches the ciphertext from the server and decrypts it locally in memory.
# Create the note once and paste the printed link into your .env.
# Use --manual-deletion, otherwise the note self-destructs on the first read.
echo "sk-1234..." | vailnote create --manual-deletion --expires-in 30d
# OPEN_AI_API_KEY=https://vailnote.com/<noteId>#auth=<authKey>
# Resolve every VailNote link in a .env file to its decrypted value.
vailnote env # prints `export KEY='value'` lines
vailnote env ./.env.local --json # machine-readable
# Load the resolved values into your shell.
set -a; source <(vailnote env); set +aNotes on this pattern:
- Create notes with
--manual-deletion- auto-delete notes self-destruct on the first read and the.envlink dies. - The
#auth=fragment is the decryption key, so.envis still sensitive. Add--passwordto requireVAILNOTE_PASSWORDat resolution time, making the link useless without it. - Notes referenced from
.envshould useecho -n(orprintf) when created, so no trailing newline is stored. - Values are single-quoted in the output, so
$and quotes in secrets survive shell sourcing.
Notes are limited to 46 KB of plaintext (the encrypted value must fit Deno KV's 64 KiB limit). llms.txt documents the HTTP API, encryption protocol, and CLI for AI agents.
This project is licensed under the MIT License - see the LICENSE.md file for details.