The problem
People and small teams need one place to keep passwords, documents and keys that is safe even if the hosting provider, the database, or the storage bucket is compromised. Most "encrypted" products encrypt at rest with a key the server holds, which protects against a stolen disk and nothing else.
What I built
I'm the primary developer at Sanders.Software: the NestJS API, the PostgreSQL schema, the React client, the storage layer on Cloudflare R2, and the key-management integration with AWS KMS.
Decisions worth mentioning
- Envelope encryption. Every file gets its own data key. That key is wrapped by a master key that lives in AWS KMS and never leaves it. A database leak on its own exposes nothing: the attacker has ciphertext and wrapped keys they can't unwrap.
- Ciphertext on R2, keys beside it. Files live on Cloudflare R2 to avoid egress fees; the wrapped key is stored alongside the ciphertext so a restore never depends on a separate key store being available.
- Postgres holds metadata, never plaintext. Names, sizes, sharing and audit history are queryable; the content isn't.
- Boring crypto. AES-256-GCM via the platform libraries, no hand-rolled primitives, and every operation is auditable per user and per object.
What I'd do differently
- Design the key-rotation path on day one. Wrapping every data key under one KMS key is simple, but re-wrapping thousands of keys when the master rotates needs a job queue and a progress marker; adding that later was more work than building it in.
- Budget for the audit log's growth. Every access is logged, which is right, but the table needs partitioning from the start rather than when it becomes the largest thing in the database.
Outcome
Live at encryptionwire.com and in production use.