feat: opt-in encryption at rest for kv and episodic - #67
Merged
Conversation
Re-applied on top of the mindD rename. Original commit 72449f8 on feat/encryption-at-rest, with module paths, the capability header and env prefix mechanically translated to the new names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds opt-in encryption at rest for the kv and episodic blocks.
This is the previously-developed encryption work, re-applied on top of the mindD rename (#65) with module paths, the capability header and the env prefix mechanically translated. It now sits directly on
main— not stacked.Design
A Driver decorator (
internal/<block>/encrypted.go) applied per-namespace at theBindseam inmain.go. No service, driver-interface or schema changes, so it works identically over the memory, Postgres and Redis backends.encryption.keys— first key is active, all remain decryption candidates, so rotation is prepend-then-retire.encrypt: true. Blocks that don't support it rejectencryptat config validation.(block, namespace[, kv key]), so ciphertext cannot be relocated across key, namespace or block. Episodic binds only to namespace, because ids/cursors are assigned after sealing.allow_plaintext_readsis the in-place migration escape hatch.DecodeSecretaccepting any length — an unset env var previously yielded a 0-byte "key".Deliberately out of scope
Documented in the concept page:
contentwould silently break the sparse/hybrid lanes (there's a GIN index onto_tsvector(content)), andUpsertmutates the caller's slice with ids the service reads back out.Size/SHA256are computed over plaintext by the service.Decorator trap worth knowing
Wrapping a
Driverhides optional capability interfaces.namespaceSizer.Sizemust be forwarded for all six blocks (semantic's has no namespace arg), as must artifact'smetaPatcher.PatchMeta— otherwise metrics/admin introspection and artifact size/sha silently break.Verification
Run against this branch after the rebase onto current
main:go build ./...,go vet ./...cleango test -race ./...— all passgolangci-lint run— 0 issuesThe build-tagged Postgres integration tests were NOT run in this environment (the container runtime backing testcontainers was unavailable). That is worth stating plainly rather than implying full coverage. The reason it is nonetheless low-risk: the encryption logic itself is unchanged from the version previously verified against a real Postgres, and the rename it was re-applied over touched only import lines in the Postgres drivers — the rename commit contains zero DDL/DML, so no table, column, index or migration name moved. Re-running those tests before merge is still the right call if a runtime is available.
🤖 Generated with Claude Code