Documentation
Self-Hosting Passwords Without Trusting the Host
Most self-hosting decisions are decisions about convenience.
You move a service onto your own hardware, accept that you are now the person who patches it, and the worst realistic outcome is some downtime and a lost evening.
A password manager is not that kind of decision.
If you already run Vaultwarden behind a reverse proxy, you have met the feeling: the machine in the corner now holds every credential you own, and you are the security team.
That is usually what stops people. Not the setup — the responsibility.
So when Everlock got a vault backend, the question I found interesting wasn't how to store passwords safely.
It was:
How little does the server need to be trusted?
Value concentrates in one place
The reason a password store feels different from a photo library is concentration.
A file share holds many things of moderate value. A password store holds one thing of enormous value, and it is the thing that unlocks everything else — including, usually, the ability to reset the accounts that would let you recover from the breach.
So the threat model is unforgiving in a specific way:
compromise the file server -> lose some files
compromise the mail server -> lose some mail
compromise the password server -> lose everything, everywhere
The standard answer is to make the server very well defended.
That is a real strategy, and it is the wrong one to rely on, because its strength is a property of your operational discipline. It degrades quietly. You will not get a notification when it stops being true.
The better move is to arrange things so that compromising the server doesn't achieve much.
The master password never arrives
Bitwarden's protocol already does this, and it is worth being concrete about what actually crosses the wire, because the whole argument rests on it.
The client takes your master password and derives keys from it locally. The server receives two kinds of thing:
client server
| |
| master password |
| | |
| derive keys locally |
| | |
| +--> master-password hash -------->| a login verifier
| | |
| +--> protected account key ------->| opaque bytes
| |
|<------------ protected account key -------|
| | |
| decrypt locally |
The master-password hash is used purely as a login verifier — it proves you know the password, and it is not the key to anything. The protected account key and the other keys are encrypted under keys derived from the master password, so the server stores them and hands them back without ever being able to open them.
Item contents follow the same rule. Names, usernames, passwords and notes reach the store only as the client's encrypted CipherStrings.
Decryption happens entirely on the client.
What the server therefore cannot do
This is the part I think deserves more attention than it usually gets, because it reads like a list of missing features and is actually the design.
"is this login verifier correct?" answerable
"has anything changed since I synced?" answerable
"give me every record for this account" answerable
"what is this item called?" not answerable
"which of these passwords are weak?" not answerable
"let this user reset their master password" not answerable
The server cannot search your vault, because it cannot read it. It cannot validate what you store, because a cipher is opaque to it. It cannot show an operator a list of your item names. It cannot offer a password reset, because there is nothing on the server that a reset could re-encrypt.
And it cannot help you if you lose your master password. The data becomes undecryptable, by design and permanently. That is the price of the whole arrangement, and it is worth saying plainly rather than burying in a footnote.
Each of those absences is a thing an attacker also cannot do with a stolen copy of the store.
What is left is a small job
Strip out everything the server can't do, and the remaining job description is short:
- check a login verifier
- hand back records for an account
- say whether anything changed
- push a notification when it does
- hold attachment and Send blobs
That is a storage service with an authentication check in front of it.
It is worth noticing how little of it is security-critical logic. There is no place in that list where a subtle bug leaks a password, because there is no point in that list where the server holds one.
Everlock's vault backend is a thin adapter over
apimeister-vault,
a standalone engine that owns the protocol, the crypto and the data
model — the same shape as the OCI registry and the photo library. The
Everlock half supplies host routing, storage and TLS. Neither half has
a reason to see plaintext.
The security boundary isn't where the middleware is
Everlock gates its backends consistently. A request arrives, HTTP Basic
auth resolves a user, a role on an access-path decides whether that
user may proceed, and the backend runs.
The vault is the one backend that deliberately does not work that way.
other backends the vault
-------------- ---------
request request
| |
Basic auth |
| |
role @ access-path | <- Everlock steps aside
| |
v v
backend client-side crypto
^ ^
| |
boundary is here boundary is here
Everlock bypasses its own HTTP middleware for the vault's vhost, because Bitwarden clients authenticate with their own master-password and token flow, and because adding a second authorization layer in front of a zero-knowledge store buys nothing it doesn't already have.
Putting a grant check there would be security theatre: it would gate access to bytes that are useless without a key the server doesn't hold.
What Everlock does control is the thing its identity model is actually
good for — who gets a vault at all. Registration is invite-gated by
default; opening it is an explicit choice. Administering an instance is
an ordinary grant on http/vault/<instance>, which lets an owner list
and delete accounts — and, because the items are ciphertext, not read
them. A future release ties registration itself to the same user
registry as the rest of the system.
That seam is in the right place. Everlock decides who gets a vault. The client's crypto decides what a vault is worth to anyone else.
You can check this instead of believing it
A hosted password manager can make exactly the same zero-knowledge claim, and mostly they do. What it cannot give you is a way to verify it.
Everlock stores each vault instance in a versioned store, which is a plain Git repository, one file per entity:
accounts/<id>.toml
ciphers/<id>.toml
folders/<account>/<id>.toml
sends/<id>.toml
blobs/<category>/<owner>/<id>
token-key.pem
So the claim is testable with tools you already have. Clone the store and read a cipher:
git clone <vault store>
|
v
ciphers/4f2a....toml
|
v
name = "2.<iv>|<ciphertext>|<mac>"
That is a CipherString where a name should be. Not a name that the server promises not to look at — a value it could not read if it tried.
I find this the most underrated property of the whole arrangement. The usual self-hosting pitch is that you get control. The more useful thing here is that you get verification: an operator with complete access to the store sees ciphertext and verifiers, and you can confirm that yourself rather than taking it on trust.
Every write being a commit also means the vault inherits ordinary Git history — when an item changed, and what it was before. Which is worth having, and worth keeping in proportion: a full history of ciphertext is still ciphertext. Turning that history into a real backup is its own subject.
Ciphertext is unusually easy to operate
The practical consequence of all this is that the boring operational questions get boring answers.
Backing up a secret store normally means building a second encrypted thing and then looking after the key to that. Here the payload already is the ciphertext, so the backup inherits the property rather than needing its own ceremony.
The hosting side is similarly small. There is no database to run alongside the vault, because versioned storage is the storage. Bitwarden clients refuse to talk to a non-HTTPS server, and the HTTP frontend obtains and renews the certificate itself, so nothing needs to sit in front of it terminating TLS. One process, one store, one hostname.
The hostname is load-bearing, and worth knowing before you pick it: the vault bakes its vhost into its token issuer and into the absolute URLs it hands back for attachments and Sends. Change it later and existing tokens stop validating until clients re-authenticate against the new one.
What this is and isn't, today
The verified path is a single-user vault, and it is verified end to end
against the official clients — registration, unlock, sync, saving an
item and reading it back in cleartext on the Bitwarden desktop app and
the bw CLI. Ciphers, folders, Sends and file attachments all work,
with real-time sync over the notifications channel so a change on one
device appears on the others.
Organizations, collections and memberships exist in the data model and the storage layer; the single-user vault is the supported path today. Emergency access, device-approval login and the paid self-hosted admin portal are not part of it.
For the reader this post is addressed to — one person, their own passwords, their own hardware — none of those are in the way.
The inversion
The instinct when self-hosting a password manager is to ask whether you can defend the box well enough.
I think that is the wrong first question, because it makes your passwords a function of your own vigilance, and vigilance is not a durable property.
The better question is what the box would give up if you lost it.
trust the host more -> the service you are leaving
trust the host less -> a server holding only ciphertext
Self-hosting your passwords is not about trusting your server more than you trust a vendor.
It is about needing to trust it less.