← All churts

uBix Vault, Beta 9 — Making It Durable

uBix Vault, Beta 9 — Making It Durable

The Ceiling Was Never a Feature

Last time, uBix Vault’s Transit engine grew into full crypto-as-a-service. That was the last big feature. Because here’s the honest truth I wrote into the roadmap this cycle: on a feature checklist, uBix Vault has matched HashiCorp Vault’s core surface for a while now — barrier, seal/unseal, KV, Transit, dynamic secrets, PKI, five auth methods, a console. Adding a sixth auth method was never what stood between it and being something you’d actually run.

What stood in the way was durability. Every byte lived in a directory on one node’s disk. Lose that disk and you lose the vault. Reschedule the pod and the data had to be dragged along with it. That is a fine shape for a sandbox and a bad shape for infrastructure. Beta 9 fixes it.

Same disclaimer, louder than usual because this beta is explicitly about making it runnable: it is a beta, not production-hardened, and it has not had an external security review. For production, use HashiCorp Vault or OpenBao. I run this on my own cluster, deliberately, alongside things I can afford to be wrong about — and I’m rolling it toward real use the careful way, low-blast-radius first.

It Can Live in a Database Now

The headline is a MySQL/MariaDB storage backend. Instead of a local directory, uBix Vault can keep its entire encrypted store in a database. The payoff is that the node becomes replaceable: the vault process can die and come back — on a different node — pointing at the same durable, replicated database, and the database’s own high-availability handles keeping the bytes safe. For a single organization that is, honestly, enough availability; you get a stateless-feeling vault pod over stateful storage you already know how to run.

Three things I care about in how it landed:

  • The database only ever sees ciphertext. This is the whole reason putting a secrets manager’s state in a shared database is even acceptable. The barrier encrypts every value with AES-256-GCM before it reaches storage, and the master key never goes near the database. So the database — and anyone who compromises it, or steals the connection string — holds ciphertext, not secrets. It’s in the trust boundary for availability, not confidentiality.
  • It’s the same one dependency. uBix Vault has had exactly one third-party library since the beginning: the MySQL driver, for the dynamic-database secrets engine. The storage backend reuses it. No Postgres driver, no Consul, no etcd, no embedded Raft — the dependency count did not move.
  • It’s a drop-in. The storage layer was always an interface with one job, and I’d written a conformance test suite that both existing backends (file, in-memory) had to pass. The database backend passes the exact same suite, against a real MariaDB in CI. That test caught a genuine bug on the first run — a subtle empty-list-versus-nil mismatch — which is precisely what that gate is for. The whole point of designing to an interface is that the new thing has to prove it’s interchangeable, and this one did, mechanically.

Setup is deliberately boring: create a database, create a user, hand it a connection string. The vault creates its own tables. On Kubernetes the connection string lives in a Secret the chart references by name, so the credentials never touch values.yaml or git — and it’s injected as an environment variable, never a command-line argument, so it stays out of the process table. That’s the “secret zero” problem every secrets manager has (it needs one credential to bootstrap); you can’t erase it, but you can shrink it, keep it out of your history, and lean on a scoped database user and TLS. The strongest version — password-less client-certificate or IAM auth to the database — is a follow-up I’ve written down.

One thing I’m careful to not oversell: this is one active writer, not multi-writer HA. The barrier key, the leases, and unseal progress all live in memory, so two vault processes against one database would fight. It’s durable, replaceable-node storage — the node can vanish and the data survives — but it is not yet a multi-node cluster. That’s the later, harder step, and the nice part is a durable database may mean I never actually need to build my own Raft to get “good enough.”

Rotating the Keys Without Taking It Down

Two operational features rounded out the beta, both the kind of thing you only miss once you depend on something.

Rekey. You can now rotate the Shamir unseal shares — mint a fresh set, with a new threshold if you want, invalidating the old ones. The obvious reason is the human one: someone who held a share leaves, and you want their share to stop mattering. The satisfying part is how it works: the unseal shares reconstruct a master key, and the master key only wraps the barrier key, which is what actually encrypts your data. So rekeying regenerates the master key and re-wraps the keyring under it — and never touches the barrier key or a single byte of data. The vault keeps serving the entire time. It’s a live operation, and it’s driven from the CLI (operator rekey) or the API, mirroring the root-regeneration flow that was already there.

Scheduled backups. Snapshots have existed since beta 2, but “there is a command you could run” is not a backup strategy. The Helm chart now has an opt-in CronJob that snapshots the running vault on a schedule and writes it to a separate volume — put that on network-backed storage and your backup survives losing the vault’s node. It authenticates with a least-privilege token that can do exactly one thing: take a snapshot. And because snapshots round-trip through the storage interface, the same mechanism migrates a vault between backends — snapshot the file-backed one, restore into the database-backed one, unseal with the same keys. That’s the on-ramp from a sandbox to durable storage.

Where It Is, and What’s Actually Left

uBix Vault is BSD 3-Clause, on GitHub at github.com/cwolsen7905/uBixVault, tagged v0.2.0-beta.9.

The road to a real 1.0 is shorter and more honest than it used to be, because the list is now down to the things that genuinely gate production rather than missing features:

  • a pluggable cloud-KMS / HSM seal, so the key that unseals the vault doesn’t have to be supplied directly;
  • and the one that actually matters most — an external security review. I can write careful code and fuzz the parsers and pass conformance suites, but I cannot audit my own crypto and have it mean anything. Until someone who isn’t me has looked hard at it, the disclaimer at the top stands.

Multi-node Raft HA used to be on that list. It’s now explicitly optional — a durable database already gives me a replaceable node, which is most of what HA was for. That’s the kind of scope honesty I want the project to keep: build the thing that removes the real risk, not the thing that looks the most impressive.

Nine betas in, the shape of it has changed one more time. It was a plan, then a design, then a binary, then a service, then a service that could authenticate anything and sign things and be its own CA — and now it’s a service whose data outlives the machine it runs on. That’s the beta where I stopped thinking of it as a demo.

← All churts