Self-hostable, end-to-end encrypted password manager built around privacy by design. Your secrets never leave your device unencrypted β the server is a cryptographically dumb blob store that never sees plaintext. Sign up with a username only β no email address, no phone number, nothing that ties your account to your real-world identity.
Four containers (Web app, Server, PostgreSQL, Redis). MIT licensed. Small enough to audit in an afternoon.
- Why BlindPass?
- Screenshots
- Features
- How Encryption Works
- Security
- Self-Hosting
- Local Development
- Architecture
- Troubleshooting
- Contributing
- License
Use BlindPass when you want the operational shape of a password manager, but not the trust shape of a SaaS account. Most password managers make a reasonable trade: polished sync, recovery, sharing, support, and enterprise controls in exchange for trusting a vendor-operated identity and storage system. BlindPass chooses a narrower trade: fewer vendor conveniences, more user control.
BlindPass is for developers, sysadmins, and privacy-pragmatic operators who care about where secrets live, what identifiers exist, and how much code must be trusted. The server is a cryptographically dumb blob store. It stores ciphertext, public material, salts, and authentication state. It does not receive plaintext vault data, a master password, a master-password hash, an email address, a phone number, or billing identity.
Your master password never leaves your device. Sign-in uses your immutable username plus an authenticator-app code; your password is used only in the browser to derive a KEK via Argon2id. That KEK unwraps your MasterKey, which unwraps VaultKeys, which unwrap ItemKeys. Keys exist only in memory and are zeroed when you lock your vault.
BlindPass is MIT-licensed and self-hostable on four containers. Your data lives on your infrastructure, under your control. The codebase is intentionally small enough to audit in an afternoon: read the code, verify the crypto, run it yourself.
- No identity exhaust β username-only accounts; no email, phone, billing profile, telemetry, or vendor account graph.
- No server-side password verifier β the server never receives your master password or a reusable master-password hash.
- Small trust surface β fewer product features than large suites, but less machinery between you and the crypto model.
- Self-hosting as default posture β four containers, no managed cloud dependency, no vendor storage backend.
- Operator control β export/import, delete, lock, revoke sessions, and run the whole system yourself.
Research checked May 9, 2026. "Zero knowledge" below means the vendor says vault contents are encrypted/decrypted client-side and unavailable to employees. It does not mean every product has the same metadata exposure, recovery model, sharing model, or server compromise resistance.
| Product | Best fit | Hosting model | Source transparency | Account identity | Master password / verifier posture | Notable strengths | Trade-offs vs BlindPass |
|---|---|---|---|---|---|---|---|
| BlindPass | Technical users who want self-hosted, low-identity password management | Self-hosted by design | MIT-licensed repo; small audit surface | Username only | Master password never leaves browser; no server-side master-password hash | No email identity, Argon2id browser key derivation, TOTP sign-in, encrypted import/export, simple deployment | Younger project; fewer native clients and enterprise controls; no browser extension by design (see ADR-0002) |
| 1Password | Families, teams, businesses needing polished apps and recovery/admin features | Vendor cloud membership | Proprietary apps with published audits and certifications | Email/account membership | Account password plus device-generated Secret Key; 1Password says both are needed to decrypt data | Mature apps, strong UX, Secret Key hardens weak account passwords, Watchtower, admin controls | Not self-hosted; 1Password 8 requires membership; email/account identity required |
| Bitwarden | Users wanting mature open source, broad clients, optional self-hosting | Vendor cloud or supported self-host | Open source/source available, third-party audits | Email account | Email + master password derive keys; Bitwarden says it never stores master password or crypto keys | Excellent client coverage, strong free/premium tiers, org sharing, self-host options including Docker/Kubernetes | Larger system to audit; self-hosting more operationally complex; email identity required |
| Proton Pass | Proton ecosystem users who want privacy features plus password management | Vendor cloud | Open-source apps, public audits | Proton account/email | Proton says data is end-to-end encrypted, including metadata | Email aliases, Proton Sentinel, open-source clients, strong privacy brand | Not self-hosted; tied to Proton account ecosystem; broader suite trust surface |
| KeePassXC | Local-first users who want no cloud account at all | Local encrypted file; user chooses sync method | GPLv3 open source | None | Local database password/key file; no remote auth service | Cloud-free, subscription-free, mature desktop app, ANSSI security visa | No built-in managed sync/sharing/server; user owns backup/sync ergonomics |
| Dashlane | Consumers and businesses wanting polished managed security features | Vendor cloud | Client transparency and security docs; not fully open source | Email/account | Dashlane says vaults encrypt/decrypt locally; Argon2d key derivation documented | Strong UX, passkeys/passwordless options, confidential computing for some enterprise flows | Not self-hosted; email/account identity required; broader managed-service dependency |
| NordPass | Users wanting simple cloud sync and modern managed UX | Vendor cloud | Proprietary with audits | Email/account | NordPass says local encryption with Argon2id-derived key and zero-knowledge architecture | XChaCha20 positioning, breach scanner, email masking, simple apps | Not self-hosted; email/account identity required; product is vendor-cloud centered |
| Keeper | Businesses needing compliance, sharing, and admin control | Vendor cloud | Proprietary with security documentation, certifications, audits | Email/account | Keeper says encryption/decryption happen locally and master password is not transmitted | Enterprise controls, record-level sharing, compliance posture, secrets manager | Not self-hosted for normal vault use; larger enterprise platform; email/account identity required |
| LastPass | Users already in LastPass or businesses needing managed migration path | Vendor cloud | Proprietary with security documentation | Email/account | LastPass says master password derives encryption/auth material; PBKDF2-SHA256 documented | Familiar UX, business features, broad platform support | 2022 incident exposed backups containing customer vault data and metadata; not self-hosted; email/account identity required |
BlindPass is not trying to beat every product on feature count. 1Password, Bitwarden, Keeper, Dashlane, Proton Pass, NordPass, LastPass, and KeePassXC all have valid use cases. BlindPass exists for a narrower reason: you want a password manager whose server cannot become an identity dossier, whose storage can live on your infrastructure, and whose implementation is small enough that trust can be replaced with inspection.
Sources: 1Password security model, 1Password Secret Key, 1Password standalone vault migration, 1Password audits, Bitwarden security white paper, Bitwarden self-hosting, Proton Pass security, KeePassXC, Dashlane security, Dashlane architecture, NordPass security, NordPass zero-knowledge architecture, Keeper encryption model, Keeper security, LastPass zero-knowledge model, LastPass 2022 incident notice.
![]() |
![]() |
| Sign in | Recovery key reveal during registration |
![]() |
![]() |
| Vault list with items of every type | Item detail |
![]() |
![]() |
| New item type picker | Settings |
Screenshots are captured by
make screenshots(Playwright). The recovery key shown is the canonical all-zeros BIP39 placeholder phrase, not a real secret.
- π End-to-end encrypted β all encryption happens on your device; the server only stores ciphertext
- π Self-hostable β own your data, run on your own infrastructure
- π Multiple vaults β organize secrets into separate encrypted vaults
- π Login items β username, password, URL, and notes
- π Secure notes β encrypted freeform text (title + content)
- π³ Payment cards β cardholder, number, expiry, CVV, and notes
- πͺͺ Identities β name, address, phone, email, and company for form-filling
- β± TOTP authenticator β issuer, secret, algorithm, digits, and period
- ποΈ Developer credentials β API tokens, client/secret pairs, and SSH keypairs with structured metadata
- πͺ Crypto wallets β BIP39 mnemonic seed phrases with optional network, derivation path, and passphrase
- π Vault sharing β share vaults with others via asymmetric key sealing
- π€ Encrypted export/import β back up and restore your vault
- π₯ Import from other managers β Bitwarden (JSON), LastPass (CSV), or Chrome passwords (CSV)
- π« No email required β accounts are identified by username only; no personal information is collected or stored
- π Recovery key β BIP39 mnemonic to regain access if you forget your password
- π’ Authenticator-based sign-in β username + TOTP for login and sensitive actions
- π« Biometric unlock (PWA) β opt-in per-device unlock via Touch ID, Face ID, Windows Hello, or Android biometric using WebAuthn PRF; wraps the master key locally and the server is uninvolved. On Android, only Google Password Manager currently supports the PRF extension β third-party passkey providers (Bitwarden, 1Password, β¦) cannot be used for biometric unlock yet (see ADR-0003 and the compatibility matrix)
- π Version history β view and restore previous versions of any item
- π Trash & restore β deleted items go to trash; restore or permanently purge them
- π Password change β re-encrypt all key material under a new master password
- π₯ Session management β view all active sessions, revoke individual sessions remotely
- β Account deletion β permanently delete your account and all associated data
- π‘ Registration gate β admin can open or close sign-ups without affecting existing accounts
- π€ User management β list users, revoke sessions, or delete accounts from the admin panel
- π Vault & item quotas β default caps (10 vaults / 1 000 items) with per-user overrides
- πͺ Password strength gate β registration rejects weak master passwords using
zxcvbnentropy scoring; short or predictable passwords are blocked regardless of character composition
- π± Web app (mobile-optimized PWA) β renders as a 430 px mobile shell on all screen sizes, optimized for the phone you are actually logging into things from; see ADR-0004
- π« No browser extension β by design; see ADR-0002
BlindPass is built on a zero-knowledge architecture β meaning we are technically incapable of reading your data, even if compelled to. The cryptography architecture was inspired by Ente's published architecture, then adapted to BlindPass's username-first account model, vault sharing flow, and self-hosted deployment target.
Sign-in uses a permanent username plus a 6-digit TOTP authenticator code. Your master password is never sent to the server β not even as a hash. It exists only in your browser, used solely to derive encryption keys.
Browser Server
β β
β POST /auth/login/start { username } β
βββββββββββββββββββββββββββββββββββββββββΆβ create login challenge state
β β
β POST /auth/login/complete β
β { username, authenticatorCode } β
βββββββββββββββββββββββββββββββββββββββββΆβ verify TOTP Β· create session
β β
βββββ 200 { message: "Authenticated" }
βββββ Set-Cookie: bp_session=β¦; HttpOnly; SameSite=Strict
β β
β GET /user/keys β encrypted key material
β Argon2id(password, kekSalt) β KEK
β decrypt(encryptedMasterKey) β masterKey [memory only]
β decrypt(encryptedVaultKey) β vaultKey [memory only]
The session cookie is HttpOnly β JavaScript on the page cannot read it. The server stores only the SHA-256 hash of the token, never the token itself. On a page reload, you re-enter your master password to re-derive keys; no OTP is required.
When you log in, BlindPass uses Argon2id β a memory-hard key derivation function designed to resist GPU and ASIC brute-force attacks β to derive a Key Encryption Key (KEK) from your master password. This derivation happens entirely in your browser. Your password is never sent over the network.
Each layer of encryption uses a unique key, so compromising one layer doesn't expose another:
your password
ββ Argon2id(password, kekSalt) β keyEncryptionKey (KEK)
ββ decrypt(encryptedMasterKey) β masterKey
ββ decrypt(encryptedPrivateKey) β privateKey (used for vault sharing)
ββ decrypt(encryptedRecoveryKey) β recoveryKey (BIP39 mnemonic)
ββ decrypt(encryptedVaultKey) β vaultKey
ββ decrypt(encryptedItemKey) β itemKey
ββ decrypt(encryptedBlob) β your plaintext secret
All keys live in memory only and are zeroed out when you lock your vault or close the tab. The one exception is opt-in Biometric unlock (per device): when enabled, the master key is stored on the device's IndexedDB wrapped by a per-credential WebAuthn PRF secret that only the platform authenticator (Touch ID, Face ID, Windows Hello, Android biometric) can release. See "What lives in your browser" below.
| Stored on server (plaintext) | Never stored on server |
|---|---|
| Username | Your master password |
| Public key (for vault sharing) | Any plaintext secret |
| KDF parameters (salt, Argon2id params) | Vault keys or item keys |
| Encrypted key material (ciphertext only) | Your private key |
To share a vault, the sender seals the vault key with the recipient's X25519 public key. The server looks up the recipient's public key by username. Only the intended recipient β holding the corresponding private key β can unseal and access the vault. The server cannot read the vault key at any point.
At registration, BlindPass generates a BIP39 mnemonic phrase (256-bit entropy) and encrypts it under your master key. If you forget your password, use this phrase to regain access. It is shown once at account creation β store it somewhere safe and offline.
All crypto uses libsodium, a battle-tested, audited library:
- Key derivation: Argon2id
- Symmetric encryption: XSalsa20-Poly1305
- Asymmetric encryption (vault sharing): X25519 + XSalsa20-Poly1305
- Recovery key: BIP39 mnemonic (256-bit entropy)
| Threat | Status | Notes |
|---|---|---|
| Server compromise | β Protected | Server stores only ciphertext β no keys, no plaintext |
| Network interception | β Protected | All data encrypted client-side before transmission |
| Brute-force via server | β Protected | No server-side password verifier or password hash to attack |
| Weak master password | Argon2id hardens derivation; a weak password is still a weak password | |
| Forgotten password | β Mitigated | BIP39 recovery key generated at registration |
| Malware on your device | β Out of scope | Client-side malware can read in-memory keys |
| Phishing / social engineering | β Out of scope | No technical control can prevent this |
| Browser extension surface | β Not shipped | No autofill or capture extension by design β see ADR-0002 |
| Local device theft + biometric bypass | When Biometric unlock is enabled, an attacker with physical device access who can defeat the chosen passkey provider's biometric gate (Touch ID / Face ID / Windows Hello / Google Password Manager on Android) can decrypt the wrapped master key on disk. Disable biometric unlock in Settings to remove this surface. |
| Property | Value | Why it matters |
|---|---|---|
| Cookie flags | HttpOnly; Secure; SameSite=Strict |
JavaScript cannot read the session token; CSRF is blocked at the browser level |
| Server storage | SHA-256 hash of token only | A database breach does not expose valid tokens |
| CSRF defense | SameSite=Strict + required x-bp-client header on mutations |
Two independent layers; a weakened SameSite assumption alone is not enough |
| Session TTL | 14 days absolute, 7 days idle | Both must hold; idle-only activity cannot extend indefinitely |
BlindPass follows a strict client storage policy β key material is never written to disk unless Biometric unlock is explicitly enabled on this device:
| Storage | What is stored | When wiped |
|---|---|---|
| Memory only | masterKey, vaultKey, itemKey, privateKey |
Lock, logout, or tab close |
IndexedDB (bp:vault-cache) |
Encrypted vault items (ciphertext only, no keys) | Lock and logout |
IndexedDB (bp:biometric-unlock) |
Wrapped master key + WebAuthn credential ID + PRF salt (only when Biometric unlock is enabled on this device) | Logout, session expiry, or explicit disenrollment in Settings (survives idle lock) |
localStorage |
Username pre-fill, theme, density preference | Never (non-sensitive) |
An XSS attack that can run JavaScript in the page cannot read the session cookie, the master key, or any vault key. The worst it can do is read the IndexedDB cache β which is ciphertext.
- Docker and Docker Compose
-
Download the compose file and configure:
curl -o docker-compose.yml https://raw.githubusercontent.com/blindpass/blindpass/main/docker-compose.prod.yml curl -o .env.example https://raw.githubusercontent.com/blindpass/blindpass/main/.env.example cp .env.example .env
Edit
.envwith your values (see the full variable reference below).Security: The example
DATABASE_URLuses the passwordblindpass. Change it before deploying to any non-local environment.Generate a base64 32-byte authenticator-secret key for the server:
TOTP_SECRET_ENCRYPTION_KEY=$(openssl rand -base64 32) -
Pull images and start all services:
docker compose up -d
No build step required β prebuilt images are pulled automatically from Docker Hub (
allisson/blindpass-server,allisson/blindpass-webapp).
The web app is served on port 8000 (HTTP). Point a reverse proxy at that port to terminate TLS β see docs/deployment/reverse-proxy.md for Caddy and nginx setup guides.
Verify the stack is running:
curl http://localhost:8000/health
# {"status":"ok","db":"ok"}Open the web app and register an account. The first registration always becomes the Admin User, regardless of whether the registration gate is open or closed.
As admin, visit /admin to:
- Open or close the registration gate β controls whether new users can sign up
- View and revoke user access
- Adjust default quotas (defaults: 10 vaults per user, 1 000 items per vault)
docker compose pull
docker compose up -d| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | β | PostgreSQL connection URL |
POSTGRES_PASSWORD |
Yes (compose) | β | PostgreSQL superuser password for the db container; must match DATABASE_URL |
TOTP_SECRET_ENCRYPTION_KEY |
Yes | β | Base64-encoded 32-byte key for encrypting stored authenticator secrets |
REDIS_URL |
Yes (prod) | β | Redis connection URL for server-side session storage |
NODE_ENV |
Yes | β | Must be production in production |
CORS_ORIGIN |
Yes (prod) | http://localhost:5173 |
Allowed origin(s), comma-separated |
PORT |
No | 3000 |
HTTP server port |
LOG_LEVEL |
No | info |
Pino log level (trace/debug/info/warn/error) |
COOKIE_DOMAIN |
No | β | Cookie domain (set when web app and API share a domain) |
COOKIE_NAME |
No | bp_session |
Session cookie name |
COOKIE_SECURE |
No | true |
Must remain true in production |
SESSION_TTL_MS |
No | 1209600000 (14 days) |
Absolute session expiry |
SESSION_IDLE_TTL_MS |
No | 604800000 (7 days) |
Idle session expiry; must be β€ SESSION_TTL_MS |
BODY_LIMIT_BYTES |
No | 524288 (512 KB) |
Maximum request body size |
DB_POOL_MAX |
No | 10 |
Maximum PostgreSQL connection pool size |
PENDING_TOTP_TTL_MS |
No | 900000 (15 min) |
How long a TOTP setup challenge remains valid |
RECOVERY_TOKEN_TTL_MS |
No | 900000 (15 min) |
How long a recovery token remains valid |
UNVERIFIED_ACCOUNT_TTL_MS |
No | 86400000 (24 h) |
How long an unverified account is retained before cleanup |
EXPOSE_DOCS |
No | false |
Enable Swagger UI at /docs |
Run BlindPass on GCP Cloud Run with external managed services β no VM to operate.
| Component | Provider | Free tier |
|---|---|---|
| Web + API | GCP Cloud Run | 2M req/month, 360K vCPU-seconds/month |
| Database | Supabase free tier | 500 MB, paused after 1 week idle |
| Redis | Upstash free tier | 10K commands/day |
| TLS + DNS | Cloud Run domain mapping | Free managed certificate |
See terraform/README.md for the full quickstart.
- Node.js 24
- pnpm 10
- Docker (for PostgreSQL)
pnpm install
make devmake dev starts PostgreSQL via Docker Compose and runs all apps in watch mode.
| Service | URL |
|---|---|
| Web app | http://localhost:5173 |
| API server | http://localhost:3000 |
make dev # start all services and apps
make test # run all tests
make test:crypto # packages/crypto only (β₯95% coverage gate)
make test:server:unit # server unit tests
make test:server:integration # server integration tests (requires Docker)
make test:e2e # e2e tests (requires make dev running)
make lint # eslint via turbo
make format # prettier --write
make ci # lint + tsc + format check + test
make db:migrate # run pending migrations
make db:studio # open Drizzle Studio
make screenshots # capture UI screenshots to docs/screenshots/
make prod:build # build production Docker images (contributors only)
make prod:up # start production stack
make prod:down # stop production stack
make prod:logs # tail production logsBlindPass is a pnpm monorepo with separate web, server, and shared package boundaries. Its zero-knowledge cryptography architecture was inspired by Ente's published architecture.
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser β
β ββββββββββββββββ β
β β apps/web β β
β ββββββββ¬ββββββββ β
β ββββββββΌβββββββββββ β
β β packages/vault ββββ packages/ β
β β (domain logic) β crypto β
β ββββββββ¬βββββββββββ β
ββββββββββββββββββββΌββββββββββββββββββββββββββββ
β HTTPS (encrypted blobs only)
ββββββββΌβββββββ
β apps/server β Fastify REST API
ββββββββ¬βββββββ
βββββββββββββ΄ββββββββββββ
ββββββΌβββββ ββββββββΌβββββββ
β Redis β sessions β PostgreSQL β ciphertext
βββββββββββ βββββββββββββββ
| Package | Role |
|---|---|
packages/crypto |
Pure libsodium primitives β Argon2id, XSalsa20-Poly1305, X25519, BIP39. No I/O, no state, no side effects. |
packages/vault |
Domain logic β encrypts/decrypts items, manages the keychain lifecycle (unlock/lock), handles vault sharing. Imports only from packages/crypto and packages/types. |
packages/api-schema |
Zod schemas for every API endpoint, shared between server and clients so validation stays in sync. |
packages/types |
TypeScript interfaces only (EncryptedValue, KeyPair, Keychain). No runtime code. |
| Layer | Technology |
|---|---|
| Runtime | Node.js 24, pnpm 10 |
| Backend | Fastify, Drizzle ORM, PostgreSQL 16 |
| Frontend | React 19, TanStack Router, Vite, Tailwind, shadcn/ui |
| Crypto | libsodium-wrappers-sumo, @scure/bip39 |
| Testing | Vitest (unit + integration), real PostgreSQL for integration tests |
| Build | Turborepo, Vite, esbuild |
make dev fails immediately
Docker must be running before starting. Start Docker Desktop (or your Docker daemon) and retry.
Port 5432 already in use
A local PostgreSQL instance is occupying the port. Stop it (brew services stop postgresql on macOS) or change the mapped port in docker-compose.yml.
make test:server:integration fails
Integration tests require Docker to be running (they spin up a real PostgreSQL container). Ensure Docker is available and no other process holds port 5432.
libsodium import errors in tests
packages/crypto must never be mocked. Always run tests against the real libsodium binding. If you see import errors, verify libsodium-wrappers-sumo is installed (pnpm install).
- Fork the repo and create a branch from
main - Run
make cibefore opening a PR (lint + type check + tests must pass) - All new code requires tests written alongside or before implementation (TDD)
packages/cryptoenforces β₯95% coverage β do not drop below it- Never mock
packages/cryptoin tests β always test against real libsodium
MIT β see LICENSE.





