feat: CipherStash Go Encryption SDK v2 - full feature parity#5
Open
calvinbrewer wants to merge 5 commits intomainfrom
Open
feat: CipherStash Go Encryption SDK v2 - full feature parity#5calvinbrewer wants to merge 5 commits intomainfrom
calvinbrewer wants to merge 5 commits intomainfrom
Conversation
Upgrade the Rust FFI layer from cipherstash-client 0.24.0 to 0.34.1-alpha.2
and add query encryption, model interface, multi-tenant keysets, multi-type
plaintext support, and structured error handling.
Rust FFI changes:
- Upgrade to cipherstash-client 0.34.1-alpha.2 with AutoStrategy/ZeroKMSBuilder
- Replace discriminated Encrypted enum with unified EqlCiphertext format
- Add GoPlaintext multi-type support (string, number, boolean, JSON)
- Add encrypt_query and encrypt_query_bulk FFI functions
- Add is_encrypted validation function
- Add lock_context grouping for bulk operations via BTreeMap
- Add keyset support for multi-tenant encryption
- Add SteVec validation (requires cast_as: json)
- Port query helpers: index lookup, query op parsing, type inference
- 71 Rust tests passing
Go SDK changes:
- Add EncryptQuery/EncryptQueryBulk for searchable encryption queries
- Add model interface (EncryptModel/DecryptModel/BulkEncryptModels/BulkDecryptModels)
- Add IsEncrypted standalone validation function
- Add KeysetConfig for multi-tenant keyset support
- Add EncryptionError with structured error codes
- Update Plaintext to interface{} for multi-type support
- Update Decrypt to return interface{} instead of string
- Update DecryptOptions.Ciphertext to accept *Encrypted instead of raw string
- Update CastAs constants to match new schema (string, number, json)
- Remove Kind field from Encrypted struct (unified format)
- Rename to "CipherStash Go Encryption SDK" in README
- 48 Go tests passing (30 protect + 18 model)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Define encryption schemas using Go struct tags instead of manually
constructing nested EncryptConfig maps. The `cs` tag now carries
the full schema: column name, cast type, and index directives.
Example:
type User struct {
ID int `json:"id"`
Email string `json:"email" cs:"email,unique(downcase),match"`
Age int `json:"age" cs:"age,cast=number,ore"`
}
config := protect.BuildEncryptConfig(protect.TableSchema("users", User{}))
Features:
- TableSchema() parses cs tags into TableDef with columns and indexes
- BuildEncryptConfig() assembles multiple TableDefs into EncryptConfig
- Type inference: string→string, int/float→number, bool→boolean, else→json
- Match defaults: ngram(3), downcase, k=6, m=2048, include_original=true
- SteVec auto-sets cast_as to json
- Parenthesized params: match(k=8,m=1024), ste_vec(prefix=t/c)
- Updated analyzeStruct to extract column name from extended cs tags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure the README to lead with the struct tag-based schema builder as the primary interface. Update the example to demonstrate the complete SDK workflow: schema definition, model encryption, query encryption, bulk operations, identity-aware encryption, and structured error handling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete rewrite of the public API surface to follow Go conventions.
The schema is now the primary API object — column references flow from
it, eliminating raw string table/column arguments at call sites.
API changes:
- Schema-driven: `users.Column("email")` provides ColumnRef, no strings
- context.Context on all I/O methods (Encrypt, Decrypt, EncryptQuery, etc.)
- Functional options: WithSchemas(), WithCredentials(), WithKeyset(),
WithLockContext(), WithServiceToken(), WithAuditContext()
- Close() implementing io.Closer (was Free())
- NewClient(ctx, ...ClientOption) replaces NewClient(NewClientOptions{})
- Sentinel errors with errors.Is() support (ErrUnknownColumn, etc.)
- Programmatic schema builder: NewSchema().Column().Equality().Done().Build()
- TableSchema() returns (*TableDef, error) instead of panicking
- `any` replaces `interface{}` throughout
- QueryType constants: Equality, FreeTextSearch, OrderAndRange, JSONSelector, JSONContains
- EncryptConfig hidden from public API — built internally from schemas
- DecryptResult.Err is error type, not *string
- Encrypted: []string/[]uint16 instead of *[]string/*[]uint16
Before:
client, _ := protect.NewClient(protect.NewClientOptions{EncryptConfig: config})
defer client.Free()
enc, _ := client.Encrypt(protect.EncryptOptions{Plaintext: "x", Table: "users", Column: "email"})
After:
client, _ := protect.NewClient(ctx, protect.WithSchemas(users))
defer client.Close()
enc, _ := client.Encrypt(ctx, users.Column("email"), "x")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Audit and fix all production-readiness issues: Concurrency safety: - Add sync.RWMutex to Client for goroutine-safe Encrypt/Decrypt/Close - acquirePtr() takes read lock; Close() takes write lock - Document Client as safe for concurrent use - var _ io.Closer = (*Client)(nil) compile-time assertion Error quality: - All JSON marshal/unmarshal errors wrapped with operation context - All context.Err() wrapped with "protect: <Op>:" prefix - Add ErrFFI sentinel for unrecognized FFI errors (inferSentinel fallback) - Model methods check for closed client before doing reflection work Naming: - CastAsJson → CastAsJSON (Go initialism convention) - CastAsJson kept as deprecated alias for backward compat API completeness: - Add ColumnOK(name) (ColumnRef, bool) — non-panicking alternative to Column() - Add IsEncrypted CGO cost documentation - Add package-level Concurrency and Credentials documentation - Document Decrypt return types (string, float64, bool, map/slice) Co-Authored-By: Claude Opus 4.6 (1M context) <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.
Summary
cipherstash-client0.24.0 -> 0.34.1-alpha.2 withAutoStrategy,ZeroKMSBuilder, unifiedEqlCiphertextformat, andGoPlaintextmulti-type supportEncryptQuery/EncryptQueryBulkfor searching encrypted columns (equality, full-text, range, JSON containment)EncryptModel/DecryptModel/BulkEncryptModels/BulkDecryptModelsusingcsstruct tags for automatic field-level encryptionKeysetConfigonClientOptsfor per-tenant cryptographic isolationEncryptionErrorwithErrorCodefor programmatic error handlingIsEncryptedvalidation: Standalone function to check if a value is encryptedBreaking changes
Encryptedstruct: removedKindfield, unified formatPlaintextfields:string->interface{}Decryptreturn:string->interface{}DecryptOptions.Ciphertext:string->*EncryptedBulkDecryptPayload.Ciphertext:string->*EncryptedCastAsconstants: renamed (big_int->bigint,real/double->number,jsonb->json, addedstring)errors.New->*EncryptionErrorwith error codesTest plan
🤖 Generated with Claude Code