Seal it. Govern it. Sign it.

minidauth takes the key out of your app. Your database holds only ciphertext, a quorum decides who can read it and can revoke that in an instant, and the completions that matter are signed by the network itself, with a key that never exists in one piece anywhere.

We added it to ten real open-source apps, from a CRM to a health-records system, without changing how anyone logs in.

Open source · MIT licensed · Node, Ruby, PHP and Python · free tier on the live network

a row in your database sealed at rest
name
ssn
email
14 of 20 machines have to agree before the key does anything

Ten open-source apps, sealed

Not a demo app. Real projects with their own logins and their own databases, forked and made to hold only ciphertext for the fields that matter. Each one seals on write and opens only for a user a quorum granted the reading role. The same pattern works in every stack we tried: Node, Ruby, PHP and Python.

Each name leads to what was sealed and how, with a link to the fork so you can read the whole change as a diff against upstream. See all projects, or tell us the app you want sealed next.

The problem it solves

Right now your app can read everything it stores. That is not a bug, it is how it is built: the data sits in your database and the key sits in your server, so whoever gets both gets everything. A stolen backup, a leaked environment file, a support session belonging to the wrong person. Nothing has to be broken for the data to walk out.

Encrypting the database does not fix it, because the thing that decrypts the database is standing right next to it. minidauth makes that key impossible to take, by making sure it never exists in one piece anywhere.

A stolen copy of your database contains nothing readable, and no key to make it readable.
A database dump with every sensitive field stored as ms1: ciphertext
A table printed straight from the database: exactly what a stolen dump returns

How it works

  1. Sealed on the way in

    Your app calls minidauth to seal a field before it is written, rather than reaching for a key. There is no key on the box to steal, so ciphertext is what reaches the database, and the full-text index, and the backups.

  2. Opened only for a granted reader

    On read, the field opens back to plaintext only for the signed-in user, and only if a quorum of your admins granted that user the reading role. No reader, or the wrong one, and it stays sealed. Even an administrator is gated.

  3. The network decides, and can refuse

    Fourteen of twenty independent machines do the crypto together and never reassemble the key. Revoke a role and reads stop everywhere at once, with no re-encryption and no change to how anyone logs in.

Refusing is the part that encryption alone cannot do. A policy carries a contract, and that contract reads your payload before the machines will sign it, so one of these comes back signed and the other does not:

// signed by the cohort
amount=250;to=alice

// refused, by fourteen machines independently
amount=5000;to=alice
An app showing a client record with three encrypted fields marked sealed, and the reader's granted role
The same app from the user's side: three fields sealed, one granted role, no second login

What changes in your code

Keep the login you already have. minidauth runs beside it, on Cognito, Clerk, Supabase, Better Auth or Auth0, whatever you run today, and the same integration file drops into each. See the integrations.

Seal on write
One hook at your data layer seals the chosen fields before they are stored. Every write path goes through the same place, so ciphertext is what lands in the database.
Open on read
The same hook opens the fields for the request's signed-in user, so an allowed reader sees plaintext and everyone else sees ms1: ciphertext.
Read roles from the network
Ask what roles someone holds on each request, instead of storing roles in your own tables. A role your app can mint is a role your app can grant itself.
// before: your database decides
if (user.role === "admin") return decrypt(record);

// after: the network decides, and can refuse
const roles = await minidauth.rolesFor(user.vuid);

Where to use it

It earns its keep when the data you hold would be bad news in someone else's hands, and you cannot honestly say your server is the last line of defence. A small team holding health records, legal files or financial detail, with no security function behind it. A product where support has to help people without being able to read their records. Any admin panel that can see everything, given that the most common breach is not a clever exploit but a valid session belonging to somebody with too much access.

Not for you if the data is public anyway, if you need analytics over plaintext server side, or if you cannot tolerate a network hop on reads. Sealed fields are also opaque to WHERE and ORDER BY, so what you seal is a deliberate choice. Those are real costs and no amount of cryptography talks you out of them.

Blog

All posts

Try it

Two commands, port 8081, no Java and no build

curl -O https://raw.githubusercontent.com/sashyo/minidauth/main/docker-compose.yml
MC_ADMIN_TOKEN=$(openssl rand -hex 32) docker compose --profile public up

minidauth is MIT and free to run. The turnkey sealing setup is one compose file and a bootstrap script; the vendor key it manages needs a Tide licence, and the free tier is not limited for any of this. Read the sealing guide first, because the first policy on a key is permanent and there is no second attempt.

Stuck anywhere, or want a second pair of eyes before you deploy that first policy? Join the Discord and I'll help you get it running.