Local-first encrypted vault — embrangle.org

A private place for your passwords and notes.
Nothing readable
without you.

Embrangle is a single encrypted file — passwords, notes, cards, bookmarks, and contacts, sealed with AES-256-GCM before anything touches disk. There's no account, no server, and nothing syncs unless you move the file yourself.

Your data is only readable with your master passphrase. It's never transmitted, never stored, and held only by you. If you lose it, the vault is permanently unreadable — that's the design, not a bug.
Storage A single .emb file you load and export yourself. No account, no server, no background sync.
Encryption AES-256-GCM throughout. The master key is derived with PBKDF2-SHA256 at 600,000 iterations.
Per-item keys Every item gets its own key via HKDF, derived from your master passphrase with a unique salt — no key is ever reused.
Developer access Architecturally impossible. There's no account, no server, and nothing leaves your device — ever.

One key unlocks the vault.
A few items can ask for more.

Most apps protect everything with a single password — break it, and the attacker has everything. Embrangle's master passphrase still protects the whole vault, but every item underneath it gets its own independently derived key, and any individual item can carry a second passphrase that survives a master-key compromise entirely.

Access gate · optional
Section passphrase
Gate an entire category — Passwords, Notes, Cards, Bookmarks, or Contacts — behind a second passphrase for the session. This is an access control, not a new encryption layer: the data underneath is still protected by the keys below.
protects against → casual access during a session, not key compromise
Layer 2 · optional
Per-item passphrase
Add a second passphrase to any individual item. It's mixed into that item's key derivation via HKDF, so the item stays locked even if your master passphrase is compromised — the one layer that survives a master-key breach.
breach of master reveals → everything except items with their own passphrase
Automatic
Per-item encryption
Once unlocked, every item's fields are sealed under their own AES-256-GCM key, derived via HKDF from the master key with a unique salt per item. No two items ever share a key.
prevents → key reuse across items
Foundation
Master passphrase
Unlocks the vault. Derived via PBKDF2-SHA256 at 600,000 iterations, it produces the AES-256-GCM key that decrypts the entire vault file in one operation.
without it → the file is just a salt and a ciphertext

For most people, the master passphrase is the foundation and the whole story — it's strong on its own. A 600,000-iteration PBKDF2 derivation makes offline brute-force attempts prohibitively slow.

Per-item passphrases are for the handful of things that genuinely warrant a second secret: a recovery seed phrase, backup codes, a single shared credential. Section passphrases are a lighter touch — useful if someone else might pick up your unlocked device.

If you forget a passphrase, whatever it protects is permanently inaccessible. That's deliberate: no one can be pressured or tricked into unlocking it on your behalf.

600k
PBKDF2 iterations
The cost of deriving the master key from your passphrase — and the cost an attacker pays per guess.
AES-256-GCM
Authenticated encryption
One audited primitive, used everywhere — the vault file, every item, every section lock — via the Web Crypto API.
5 min
Idle auto-lock
The vault locks itself after 5 minutes of inactivity. Copied fields clear from the clipboard after 30 seconds.

How items are stored

Every item — a login, a card, a contact — is a title plus a list of fields. Each field is tagged with a single-character symbol that tells the app how to handle it: whether it's masked, copyable, a URL, or a TOTP secret. This field array is what gets sealed under the item's own key; the app rebuilds the UI from it after decryption.

login
{
  "type": "login",
  "title": "Google",
  "fields": [
    { "sym": ">", "val": "https://accounts.google.com" },
    { "sym": "=", "val": "email@example.com" },
    { "sym": ":", "val": "••••••••••" },
    { "sym": "*", "val": "TOTP_SECRET", "mods": ["!"] }
  ]
}
card
{
  "type": "card",
  "title": "Personal Visa",
  "fields": [
    { "sym": "@", "val": "Name On Card" },
    { "sym": "$", "val": "1234 5678 9012 3456", "mods": ["!"] },
    { "sym": ":", "val": "CVV", "mods": ["!"] },
    { "sym": "^", "val": "12/27" }
  ]
}
contact
{
  "type": "contact",
  "title": "John Smith",
  "fields": [
    { "sym": "@", "val": "John Smith" },
    { "sym": "+", "val": "+44 7700 900000" },
    { "sym": "=", "val": "john@example.com" },
    { "sym": "~", "val": "met at conference 2024" }
  ]
}

An item's type and title are what populate the hub and item list once the vault is unlocked. The fields array is different — it stays sealed under the item's own key until you open that specific item, and is decrypted only into memory.

Two field-level markers add extra protection beyond encryption: ! (Permanent) warns before copying or revealing a value you likely can't rotate — recovery codes, seed phrases. ; (Re-auth) requires your passphrase again before that specific field can be revealed or copied, even within an already-unlocked vault.

Revealed fields re-mask themselves after 45 seconds, and anything copied to the clipboard is wiped after 30 — whether or not you come back for it.

@Username / IDPlaintext, copy on tap
:Password / secretMasked, generator built in
=Email addressPlaintext, copy on tap
+Phone numberPlaintext, copy on tap
>URLTappable, opens in a new tab
~Freetext / notePlaintext, no special behavior
*2FA / TOTP secretMasked, generates a live 6-digit code
^Date / expiryWarns as the date approaches
$Financial / accountMasked, copy on tap
-List itemRendered as a list row

One file.
That's the whole vault.

Your vault is a single .emb file containing two things: a salt, and an encrypted blob. Every item, folder, title, and section lock lives inside that one ciphertext. Anyone who got hold of the file — even with full knowledge of how Embrangle works — would find a salt and a string of bytes, and nothing else.

vault.emb
keycontents
v vault format version
masterSalt PBKDF2 salt, hex
masterCt AES-256-GCM ciphertext, base64

A plain JSON file — readable structure, unreadable contents.

  • The file is portable. Copy it, back it up, or move it to another device — there's nothing to configure on the other end, just the file and your passphrase.
  • If the file is intercepted, there's nothing to read: no item count, no titles, no folder names, no section names — just a salt and a ciphertext blob.
  • Nothing runs in the background. No server, no account, no sync. Open the file, work, and export when you're done.
  • Embrangle doesn't autosave to disk. Changes live in memory until you export — closing the tab without exporting discards them.

Built to be verified.

Embrangle runs as a web app — which means the code running on your device is readable. Open your browser's developer tools and watch the network tab: nothing is sent anywhere, ever. You can see exactly what gets encrypted, when, and with what. That level of transparency is harder to offer in a native app, and it's a deliberate choice.

Layer Technology Role
Frontend HTML / CSS / JS Single-page app. No build step, no framework, no dependencies.
Encryption Web Crypto API PBKDF2, HKDF, and AES-256-GCM. Entirely client-side, never leaves the device.
2FA codes Web Crypto (HMAC-SHA1) Standard TOTP, generated locally from a stored secret — nothing phones home.
Storage .emb file (JSON) Loaded and exported manually. No database, no server, no background sync.

What Embrangle
doesn't do.

These aren't oversights — they're the tradeoff for not trusting anything beyond your own passphrase. Every item below would require an account, a server, a background connection, or some other party in the loop.

Cloud sync or accounts
Browser extension or autofill
Server-side storage of any kind
Push notifications
Collaboration or sharing
Calendar integration
Automatic backups
Telemetry or analytics

Backups, multi-device access, and recovery are all on you — by design.