← All projects

Case study

09 / 2026

Velve Auth

We wrote the operating system. The login was not going to be someone else's.

Context

The European authentication library for TypeScript and PostgreSQL, built for Velve, a deployment platform for Europe that is written from the ground up down to its own kernel. It runs in your own process with your users in your own database, answers who is signed in, and nothing else: no roles, no organisations, no third-party service in the loop.

Why I built it

Velve is built from scratch. Not the framework, not the runtime the operating system underneath is ours, and the platform on top is shaped differently from anything a general-purpose auth library expects. Sign-in sits in the core of that platform, and the things the platform will need from it later are things no off-the-shelf library has a place for. Borrowing was never really on the table, and the one candidate settled the question on its own: Better Auth stores password hashes as salt and hash with no algorithm marker, a grep for rehash across its whole repository returns nothing, it invents email addresses at nine call sites when a provider returns none and since July 2026 it belongs to Vercel.

I wanted a library that verifies whatever hash you already have and quietly replaces it with Argon2id on the next sign-in.

Verifying a hash and creating one are separate decisions. The library reads six prefix families Argon2, bcrypt, scrypt, PBKDF2 and Firebase's modified scrypt and writes exactly one.

Auth libraries don't fail on cryptography. Of Better Auth's 33 published advisories, ten are the same missing line: and user_id equals actor. So every repository method on a user-bound table takes an actor in its signature. You can pass the wrong one; you can't forget it.

The optional WASM accelerator broke the one guarantee it was meant to speed up. hash-wasm settles in a microtask, so 800 concurrent sign-ins against a five-second wait limit ran 14.7 seconds with zero refusals. The fix yields between derivations, and the wrong explanation I first wrote for it is still in the decision log above the correction a log you tidy is a log you can't trust.

What should've been a library is currently a 290-entry decision log, 4,300 lines of documentation and sixteen tables, with the sign-in route still ahead of it. The reasons are the product; the code is the receipt.

Stack

TypeScriptPostgreSQLNode.jsArgon2idWebAuthn@noble/hashesGitHub Actions

Links

Next