zeekay c9b170c3c4 Carry the header a copy does not inherit, and prove it
sqlcipher_export copies the schema and every row — including the internal
AUTOINCREMENT counters — but a fresh database starts its HEADER at zero, so
user_version and application_id were silently reset to 0 by a conversion.

Measured, not reasoned about: fidelity_test.go builds a database holding an
AUTOINCREMENT counter, a WITHOUT ROWID table, a generated column, a foreign
key, an index, a view, a trigger and both header fields, converts it, and
compares the two by asking the database itself. Everything else survives; these
two did not.

For the identity store this was harmless — neither hanzoai/orm nor hanzoai/iam
reads user_version — but Convert is a general primitive, and a store whose
migration framework keys on user_version would have come back convinced it was
looking at version 0.

They are carried explicitly now, and the parity check compares them, so the fix
is held up by the same gate as the rows rather than by this message.

Also refuse to commit a copy that still has a write-ahead log beside it. Only
the main file is renamed, so the copy has to be complete in it; closing the last
connection checkpoints and removes a WAL, and one still present means the close
did not finish. Refusing costs a retry on the next boot, renaming anyway costs
whatever the log still held.
2026-08-11 10:36:31 -07:00

cek

Opens a namespace's SQLite database encrypted at rest.

cek.SetMaster(k)                            // once, at boot, from KMS
db, err := cek.Open(ns, "treasury", dataDir) // everywhere else

The key is derived from the master and the namespace. It is not generated, not wrapped, not stored, and not rotated in place — so there is no unwrap step, no rewrap step, no per-file key material to lose, and no migration path to maintain. A database is born encrypted or it does not exist.

Losing the master loses the data. That is the property you want from encryption at rest, and the reason the master lives in KMS.

Where it sits

hanzoai/namespace names the entity, and where its file lives
hanzoai/cek turns the master + that name into the file's key, and opens it
hanzoai/sqlite opens a file under a raw key; knows nothing about who owns it
hanzoai/kms holds the master

Nothing here knows about orgs, users, billing or plugins. It knows a namespace, a subsystem, and a master key.

S
Description
No description provided
Readme MIT
128 KiB
Languages
Go 100%