The CyborgDB Go SDK is the Go client for CyborgDB — the vector database that stays encrypted even while it's searching. Run similarity search directly on encrypted data with client-side keys; only the result of a query is ever decrypted, never the index. Built with Go's type system and context support for services and microservices.
This SDK talks to cyborgdb-service, which you self-host in your own VPC or on-prem and run alongside your app. Install and start it separately. See our docs for more info.
- Encryption-in-use: Search runs directly on ciphertext — only the query result is decrypted, never the index or stored vectors
- Encrypted ANN: Disk-backed encrypted DiskIVF index with recall within 2% of a plaintext baseline (read the benchmarks)
- Filters on encrypted metadata: Combine vector similarity with equality and range predicates in a single request
- BYOK / HYOK: Wrap per-index keys with AWS KMS or AWS Secrets Manager, or hold the key client-side — you control the key material
- Per-tenant key isolation: Per-index, per-user keys with cryptographic RBAC; revoke a user and their keys are erased
- Idiomatic Go API: Strong typing with
contextsupport for cancellation and timeouts
To get started in minutes, check out our Quickstart Guide.
- Install
cyborgdb-service
# Pull the CyborgDB Service image
docker pull cyborginc/cyborgdb-service- Install
cyborgdbSDK:
go get github.com/cyborginc/cyborgdb-gopackage main
import (
"context"
"fmt"
"log"
cyborgdb "github.com/cyborginc/cyborgdb-go"
)
func main() {
// Initialize the client
client, err := cyborgdb.NewClient("https://localhost:8000", "your-api-key", false)
if err != nil {
log.Fatal(err)
}
// Generate a 32-byte encryption key
indexKey, err := cyborgdb.GenerateKey()
if err != nil {
log.Fatal(err)
}
// Create an encrypted index
dimension := int32(128)
createParams := &cyborgdb.CreateIndexParams{
IndexName: "my-index",
IndexKey: indexKey,
Dimension: &dimension,
}
index, err := client.CreateIndex(context.Background(), createParams)
if err != nil {
log.Fatal(err)
}
// Add encrypted vector items
items := cyborgdb.VectorItems{
{
Id: "doc1",
Vector: []float32{0.1, 0.2, 0.3}, // ... 128 dimensions
Metadata: map[string]interface{}{
"category": "greeting",
"language": "en",
},
},
{
Id: "doc2",
Vector: []float32{0.4, 0.5, 0.6}, // ... 128 dimensions
Metadata: map[string]interface{}{
"category": "greeting",
"language": "fr",
},
},
}
err = index.Upsert(context.Background(), items)
if err != nil {
log.Fatal(err)
}
// Query the encrypted index
queryVector := []float32{0.1, 0.2, 0.3} // ... 128 dimensions
queryParams := cyborgdb.QueryParams{
QueryVector: queryVector,
TopK: 10,
Include: []string{"metadata"},
}
response, err := index.Query(context.Background(), queryParams)
if err != nil {
log.Fatal(err)
}
// Print the results. For a single-vector query the matches are in
// Results.ArrayOfQueryResultItem (batch queries populate
// ArrayOfArrayOfQueryResultItem instead).
if response.Results.ArrayOfQueryResultItem != nil {
for _, result := range *response.Results.ArrayOfQueryResultItem {
fmt.Printf("ID: %s, Distance: %f\n", result.Id, result.GetDistance())
}
}
}// Search with multiple query vectors simultaneously
queryVectors := [][]float32{
{0.1, 0.2, 0.3}, // ... first vector
{0.4, 0.5, 0.6}, // ... second vector
}
queryParams := cyborgdb.QueryParams{
BatchQueryVectors: queryVectors,
TopK: 5,
Include: []string{"metadata"},
}
batchResults, err := index.Query(context.Background(), queryParams)
if err != nil {
log.Fatal(err)
}// Advanced metadata filtering with operators
complexFilter := map[string]interface{}{
"$and": []map[string]interface{}{
{"category": "greeting"},
{"metadata.score": map[string]interface{}{"$gt": 0.8}},
{"language": map[string]interface{}{"$in": []string{"en", "fr"}}},
},
}
queryVector := []float32{0.1, 0.2, 0.3} // ... your query vector
nProbes := int32(1)
greedy := false
queryParams := cyborgdb.QueryParams{
QueryVector: queryVector,
TopK: 10,
NProbes: &nProbes,
Greedy: &greedy,
Filters: complexFilter,
Include: []string{"distance", "metadata", "contents"},
}
results, err := index.Query(context.Background(), queryParams)When the service is configured with a kms.registry entry, the SDK can
delegate key management entirely to the server-side KMS. The service
generates the data encryption key, wraps it under the named KMS slot, and
persists the envelope — the SDK never sees or holds the key.
// Create a KMS-backed index — no IndexKey from the SDK side.
// "vendor-kms-slot" must match an entry in the service's cyborgdb.yaml.
kmsName := "vendor-kms-slot"
dimension := int32(128)
metric := "euclidean"
index, err := client.CreateIndex(ctx, &cyborgdb.CreateIndexParams{
IndexName: "kms-backed-index",
KmsName: &kmsName,
Dimension: &dimension,
Metric: &metric,
})
// Reopening the index later doesn't require a key either; the service
// resolves the data key from the index's stored KMS envelope.
loaded, err := client.LoadIndex(ctx, "kms-backed-index", nil)For provider: none registry entries, the SDK still supplies the KEK on
every call — pass both IndexKey and KmsName:
kmsName := "plain" // registry slot with provider: none
index, err := client.CreateIndex(ctx, &cyborgdb.CreateIndexParams{
IndexName: "sdk-keyed-index",
IndexKey: indexKey,
KmsName: &kmsName,
Dimension: &dimension,
})How slots are configured. A
kms.registryslot is added to the service'scyborgdb.yamlby your cyborgdb-service operator — not from the SDK. Each slot declares one provider (aws-kms,aws, ornone) plus the AWS identifiers needed to wrap/unwrap data keys. For real-KMS slots (aws-kms/aws), set-up also requires IAM work on the customer's AWS account; seeBYOK.mdin the cyborgdb-service repo for the full operator + customer walkthrough. From the SDK side, you only need the slot name your operator provisioned.
For more information on CyborgDB, see the Cyborg Docs.
The CyborgDB Go SDK is licensed under the MIT License.