mirror of
https://github.com/hanzoai/cek.git
synced 2026-10-09 02:37:18 +00:00
8a45f8af0411a273e2570f774a10e9088d96e81a
The copy was SQLCipher's sqlcipher_export, a SQL function only the C libsqlcipher defines. So a build without that library could not convert a plaintext database at all: the store it was pointed at stayed unopenable, and every test covering the conversion skipped rather than ran. Nothing else here needed the C engine. github.com/hanzoai/sqlite encrypts on every build — the pure-Go SQLCipher codec envelope, byte-compatible with the C one — so the conversion was the last place the dependency survived, and it did not have to. It is now a logical copy in Go over two ordinary handles: the plaintext source opened with no key, a fresh encrypted target opened with one. Tables, then rows, then indexes and triggers and views, so an index is not rebuilt per insert and a trigger cannot fire on rows the source never had. Columns are named from table_xinfo rather than SELECT * — a generated column cannot be inserted into, and naming them also stops a normalised column order from writing values into the wrong column. The AUTOINCREMENT counters come across from sqlite_sequence, which takes a delete and an insert because that table carries no unique index for an ON CONFLICT target to name. A byte-level encrypt is not available for this: SQLCipher reserves 80 bytes per page for its IV and tag, a plaintext database reserves none, and no pragma retrofits that into an existing file. A virtual table is REFUSED, naming itself. Its shadow tables hold an index layout rather than rows, so recreating it regenerates bytes no logical copy can promise and verify would report a difference the data does not explain. The C function did that from inside the engine. The suite now runs on the pure-Go build with nothing skipped — 30 tests, including fidelity over AUTOINCREMENT, WITHOUT ROWID, a GENERATED column and foreign keys — and passes identically against a real libsqlcipher. A store converted by either engine reads back under the other, rows, header fields and counters intact. Co-authored-by: Hanzo Dev <[email protected]>
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.
Languages
Go
100%