Documentation
¶
Overview ¶
Package cek is cloud's ONE encryption-at-rest gate for its per-subsystem SQLite stores. Every store opens its database through cek.Open — the single seam where a plaintext file is transparently migrated to SQLCipher and a keyed *sql.DB is returned — so "encrypted at rest" is a property of the open path, not something each of the ~30 stores must remember to do.
THREAT MODEL. The DO block volume under /var/lib/cloud is provider-encrypted, so the residual exposure is a COPIED PV snapshot/backup or an in-cluster exec/PV read seeing plaintext customer PII (crm), the ledger (treasury), org wallet maps (wallets), the audit log, team/entitlements. cek removes that exposure: each file's pages are SQLCipher-encrypted under a per-database key that never leaves the process in the clear and is itself wrapped by the KMS-injected master key; the pre-migration plaintext copy is shredded once the encrypted store is proven readable. A lifted file is useless without the key.
SCOPE / NON-GOALS. cek provides CONFIDENTIALITY at rest against the read-only exposure above (no master key ⇒ no plaintext). It is NOT integrity, authenticity, or anti-rollback against a PV-WRITE (node-compromise) adversary who can modify the volume: the per-file id lives in the (unauthenticated) .dek sidecar, so such an adversary could swap two of OUR OWN {db,.dek} pairs or replay an old snapshot. That is outside the stated model and is deliberately NOT defended here (a logical-id+epoch binding would add complexity for an out-of-model threat); revisit only if tenant-isolation-under-node-compromise is scoped in.
ENVELOPE (the primitives live in github.com/hanzoai/sqlite/cek.go and are reused verbatim — one crypto implementation, KAT-gated there):
- Each database has its OWN random 256-bit DEK (the SQLCipher page key), minted once at first touch and NEVER changed, so ciphertext pages are never rewritten.
- Each database also gets a random 128-bit FILE ID, stored in the clear at the head of its <db>.dek sidecar. The KEK is derived from that id, NOT the file path: KEK = HKDF-SHA256(masterKey, lp("global") || lp(hex(fileID))). The id is intrinsic to the file and travels with the sidecar, so moving the data dir or changing CLOUD_DATA_DIR can never change the KEK and brick a store. RFC-5869 HKDF via x/crypto/hkdf — NOT luxfi/crypto/kdf (a QZMQ KeySchedule, not generic HKDF; using it would brick every store).
- The DEK is wrapped AES-256-GCM under the KEK, bound to the same id as AAD. Sidecar = fileID(16) || wrapped-DEK. The raw DEK is never written.
- Master-key ROTATION rewraps only the sidecar: the DEK and fileID are unchanged, so no page is rewritten and no file can be bricked.
FAIL-SECURE. On an encryption-CAPABLE build (production is CGO + libsqlcipher) a missing master key is FATAL — the data plane refuses to open unencrypted, the same posture the KMS store takes. A key set on a NON-encrypting build is likewise fatal. An encrypted file whose sidecar is missing is refused. A migration whose encrypted copy does not reproduce the source's schema AND per-table content (a rowid-independent multiset hash, not just a row count) leaves the plaintext untouched and errors — the caller (MountAll) fails closed, so cloud never serves a half-migrated data plane.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Encrypting ¶
func Encrypting() bool
Encrypting reports whether cek will encrypt at rest (a valid master key is configured on an encryption-capable build). cloud calls this once at boot for the posture log; a false result on a capable build means resolveMaster errored and the first store Open will fail closed.
func Open ¶
Open returns a *sql.DB for the SQLite database at path, encrypted at rest when a master key is configured. It is the single drop-in replacement for sql.Open("sqlite", path) across every cloud store.
func SetMasterKey ¶
func SetMasterKey(k []byte)
SetMasterKey injects the 32-byte master key explicitly (cloud's boot resolves it once from cfg and hands it here), taking precedence over the environment. Call before the first Open. A wrong-length key is ignored so the env path can still apply.
Types ¶
This section is empty.