Replace SHA-256 key derivation with HKDF and per-row salt for stored API keys

Every stored user API key in the Mike backend is currently encrypted with the same derived key - one SHA-256 hash of the master secret. bmersereau replaces that with HKDF (RFC 5869) and a random 16-byte salt per row, so each encryption is independent.

securityinfrastructure

The problem with the current scheme is straightforward: if that single derived key is compromised, all encrypted rows are exposed at once. Per-row HKDF salts isolate the damage to individual rows.

Migration is additive. A nullable salt column is added to the user API keys table. Rows that predate the migration have null for salt and continue to decrypt through the legacy SHA-256 path. New writes and any updated rows transparently move to HKDF. There's no one-shot re-encryption pass required to get the new behavior.

The PR ships unit tests for the new helpers: encrypt/decrypt round-trip, uniqueness of salt and IV across calls, tamper detection on ciphertext, and the legacy null-salt compatibility path. A vitest test runner is also introduced as part of the backend dev setup. Closes issue #67.

So what Worth taking if you're running Mike in production with real user API keys stored. The null-salt compatibility path means existing rows keep working, and new rows get stronger isolation immediately after the migration lands.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?