Self-hosting is hereRead more →
← Back to blog

Why zero-knowledge encryption makes ArmoryHub easier to build, not harder

Why zero-knowledge encryption makes ArmoryHub easier to build, not harder

When I tell people ArmoryHub is zero-knowledge, end-to-end encrypted, the usual reaction is sympathy. Surely that makes everything harder? No server-side search, no peeking at data to debug a problem, no easy analytics.

Some of that is true, and I'll get to the trade-offs below. But after a year of running this as a solo developer, I'd say the encryption model has made the app easier to build and cheaper to run than the conventional design would have been. I started building it because I wanted privacy for my own collection, and the practical benefits were a pleasant surprise.

First, how it works in two paragraphs

Your PIN, or an optional longer passphrase, is run through a key-derivation function on your device to produce a key. That key unlocks a randomly generated master key, and the master key encrypts everything you store: each firearm, each box of ammunition, each photo and document. All of that happens on your phone, tablet or computer before anything is sent anywhere.

What reaches my server is ciphertext. The database holds scrambled blobs that I can't read, and neither could anyone who got into it. That one design decision is where all the benefits below come from.

The server doesn't need to understand your data

In a conventional app, the database knows what a firearm is. There's a table with columns for manufacturer, caliber, serial number, purchase price and so on. Every time you want to track something new, you write a migration, test it against production-sized data, plan for downtime or a backfill, and hope nothing goes wrong on the way.

ArmoryHub doesn't work that way. Each record is stored as one encrypted payload, and the server treats it as an opaque blob with a type and a few bookkeeping fields. The structure of a firearm record lives inside the payload, and only the app, running on your device, knows what's in it.

So when I add a field, say a new maintenance option or another way to describe an optic, that's an app change and not a database change. New versions of the app write the new shape. Older records are read with sensible defaults and quietly upgraded the next time they're saved. There's no migration to run across everyone's data, and no window where the code and the schema disagree. I still add database tables occasionally for genuinely new kinds of object, but the everyday feature work, which is most of what I do, never touches the schema.

For a solo developer, that's a big deal. A lot of the fear in shipping software comes from changes that are hard to undo. Here, most changes are easy to undo.

Your device does the work, so the server bill stays small

Because the server can't read your data, it can't do anything clever with it. It can't total up your inventory value, filter by caliber, build a report or draw a chart. All of that happens on your device, after decryption.

That sounds like a limitation, and for a company running a giant dataset it would be. But a firearms collection is small. Even a big one is a few hundred records, which any modern phone can sort, search and chart instantly. The dashboard, the value breakdowns, the reports, the PDF and CSV exports, the ballistics calculator and the caliber filters all run locally.

The result is that my hosting costs are tiny. The server stores and returns blobs, enforces who can access which rows, and handles sign-in. It doesn't run queries, build reports or crunch numbers. When the user count crossed a thousand, the infrastructure bill barely noticed, because almost none of the new work lands on my side. You are, in effect, supplying your own compute.

A very small API

If the server only stores and returns encrypted blobs, there isn't much of an API. There's no endpoint for "get my ammo grouped by caliber" or "generate my insurance report." Each of those is logic in the app that runs on your device.

Fewer endpoints means fewer things to design, test, secure and keep consistent. It also means the logic lives in one place, so there's no risk of the server and the client calculating the same number two different ways and disagreeing.

A smaller thing to protect

I run ArmoryHub on Supabase and Vercel rather than my own servers, and I'm happy that's the case. I don't want to be the person responsible for defending a database full of readable firearms records, and under this design I'm not. What I'm guarding is ciphertext.

It also changes how I think about the unpleasant scenarios. If the database were breached, the attacker would get encrypted blobs and the salts needed to attempt guessing PINs offline, which is why the server rate-limits attempts and why a longer passphrase is available for people who want more margin. If someone served me with a legal demand, there's nothing readable I could hand over. I don't have to make promises about how carefully I'll behave, because I'm not able to read the data in the first place.

It's also much harder to switch off

This one surprised me, and it's become one of the most important benefits.

Apps that deal with firearms live on other people's platforms: app stores, hosting providers, payment processors. Each has its own rules about firearms-related content, and those rules get interpreted differently from one review to the next. I can't control that. What I can control is what those platforms are actually looking at.

The ArmoryHub iPhone app contains no firearm data. None. It's a client, a native wrapper around the same web app, that signs you in, downloads encrypted blobs and decrypts them on your device so you can read them. Think of it as a browser with a very specific job. There's no catalogue of firearms inside it, no database of products, nothing for sale and nothing to browse, only the records you put there yourself, in a form only you can open.

The same goes for hosting. Supabase doesn't host firearm data, because it can't. It hosts ciphertext, which is a pile of encrypted blobs that it has no way to read, alongside the things any account system needs: an email address, how many records exist and when they changed. The same is true for Vercel, which serves the app itself and never sees your collection.

I'm not suggesting that makes every conversation with a platform easy. It does mean I can have those conversations honestly. A reviewer, a host or a payment processor looking at ArmoryHub is looking at a user interface and some encrypted data, not a firearms database.

It's also a form of insurance. Because the app is a web app first, with the iPhone app as a wrapper around it, no single door is the only door. If one distribution channel ever disappeared, the web app and the installable version would still work against the same account and the same encrypted data, and so would a self-hosted install.

Self-hosting came almost for free

Because the server is basically a blob store with authentication, the self-hosted version is the same code with a different place to keep the blobs. Moving between hosted and self-hosted doesn't involve re-encrypting anything, because the ciphertext moves untouched and your PIN unlocks it on the other side. That would have been a much bigger project if the server had its own opinions about the data.

Support is light

When I can't see your data, I also can't be expected to fix it by looking at it. That sounds like a downside, but it has pushed the product toward being predictable and self-explanatory, because it has to be. I get around two support emails a week, and most are small feature requests that I can usually ship the same day.

It isn't free, though

I want to be straight about the costs, because they're real.

I can't search your data on the server, so features that depend on it, like cross-user analytics or server-side notifications built from your records, are off the table. Debugging can mean asking for a screenshot instead of looking at the record myself. Everything runs on your device, so a very old phone with a very large collection will feel it more than a new one. And the app currently needs an internet connection, because offline support isn't built yet.

I think those are good trades for a product like this, but they are trades.

The caveat, and it matters

Everything above is good for you as a customer and good for me as a developer. The same design has one consequence you need to understand before you rely on it.

If you lose your PIN, or your passphrase if you've enabled one, your data is gone. I can't recover it.

I mean that literally. I don't hold your keys, there is no reset link that brings your records back, and there's no support ticket that fixes it. The same property that means I can't read your collection means I can't unlock it for you either. Your data stays locked until you remember the PIN or passphrase, if you ever do.

A few practical suggestions. Choose something you'll remember, and don't treat a longer passphrase as automatically better, because one you forget is worse than a shorter one you don't. If you write it down, keep it somewhere safe, not on the device it unlocks. A password manager works well, as does telling a trusted person in your household where to look. And try your PIN occasionally, so you find out it's slipped your mind while there's still time to export or reset things.

That trade is the whole point of the design, and I'm comfortable with it. I just want you to be comfortable with it too.

Marc Wrigglesworth is the founder and sole developer of ArmoryHub. Questions are welcome at marc@armoryhub.app.