Skip to main content

SochDB gRPC Server Architecture

The SochDB gRPC server (sochdb-grpc-server) is the thick-server / thin-client network surface for SochDB v2. It is built on tonic (Rust gRPC over HTTP/2) and exposes the full v2 feature set — vector search, graph, collections, namespaces, semantic cache, tracing, checkpoints, MCP, key-value, and change-data-capture subscriptions — as 12 gRPC services plus a standard gRPC health service.

In addition to the gRPC port, the same binary can serve a Prometheus metrics endpoint, a WebSocket gateway, and a PostgreSQL wire-protocol port. See Deploying to Production for Docker, Helm, and Kubernetes operations.

Quick start

# Build the server
cargo build --release --package sochdb-grpc --bin sochdb-grpc-server

# Run on the default loopback port (50051), no auth
sochdb-grpc-server

# Bind all interfaces with authentication enabled
sochdb-grpc-server --host 0.0.0.0 --port 50051 --auth
Container image

The published image runs the same binary: docker run -p 50051:50051 sochdb/sochdb-grpc:latest. The image's ENTRYPOINT is sochdb-grpc-server with default CMD --host 0.0.0.0 --port 50051.

CLI flags and default ports

All flags are defined on the clap Args struct in sochdb-grpc/src/main.rs.

FlagDefaultEnv varPurpose
--host127.0.0.1Bind address
-p, --port50051gRPC port
-d, --debugoffDebug logging
--metrics-port9090Prometheus HTTP /metrics port (0 disables)
--ws-port8080WebSocket gateway port (0 disables)
--pg-port5433PostgreSQL wire-protocol port (0 disables)
--authoffEnable gRPC authentication
--api-keynoneSOCHDB_API_KEYRegister an API key (requires --auth)
--tls-certnoneSOCHDB_TLS_CERTTLS cert PEM path (enables TLS)
--tls-keynoneSOCHDB_TLS_KEYTLS private-key PEM path
--tls-canoneSOCHDB_TLS_CACA cert for mTLS client verification
--secrets-pathnoneSOCHDB_SECRETS_PATHKubernetes Secrets mount path
--pg-data-dirnoneSOCHDB_PG_DATA_DIRPersistent dir enabling real SQL via the PG wire port

Additional environment variables are read directly (they are not clap flags): SOCHDB_API_KEY_PEPPER, SOCHDB_JWT_SECRET, SOCHDB_ENCRYPTION_KEY, and SOCHDB_API_KEYS (comma-separated).

No --config flag

The server is configured entirely through the flags and environment variables above. There is no --config flag — the legacy sochdb-server-config.toml and the make server-run target that references it are stale and do not work against the current binary.

Port summary

ServiceDefault portNotes
gRPC50051All 12 services + gRPC health
Prometheus metrics9090GET /metrics, GET /health; 0 disables
WebSocket gateway8080JSON message protocol; 0 disables
PostgreSQL wire5433Simple-query only; 0 disables

The 12 gRPC services

When the server starts it registers the following services on the gRPC port (main.rs). Every service except VectorIndexService is wrapped with the auth interceptor. A standard tonic_health service is also mounted and is not behind auth so that Kubernetes and load-balancer probes can reach it.

#ServiceAuth interceptorNotable RPCs
1VectorIndexServiceNo (64 MB messages)CreateIndex, InsertBatch, InsertStream, Search, SearchBatch, GetStats
2GraphServiceYesAddNode, AddEdge, Traverse, ShortestPath, AddTemporalEdge, QueryTemporalGraph
3PolicyServiceYesRegisterPolicy, Evaluate, ListPolicies, DeletePolicy
4ContextServiceYesQuery, WriteEpisode, EstimateTokens, FormatContext
5CollectionServiceYesCreateCollection, AddDocuments, SearchCollection, GetDocument
6NamespaceServiceYesCreateNamespace, ListNamespaces, SetQuota
7SemanticCacheServiceYesGet, Put, Invalidate, GetStats
8TraceServiceYesStartTrace, StartSpan, EndSpan, AddEvent, GetTrace
9CheckpointServiceYesCreateCheckpoint, RestoreCheckpoint, ExportCheckpoint, ImportCheckpoint
10McpServiceYesRegisterTool, ExecuteTool, ListTools, GetToolSchema
11KvServiceYesGet, Put, Delete, Scan (server-stream), BatchGet, BatchPut
12SubscriptionServiceYesSubscribe (stream), WatchKey (stream), ListSubscriptions, CancelSubscription
VectorIndexService is special

VectorIndexService is the only service registered without the auth interceptor, and it is the only one configured with a 64 MB max encode/decode message size (max_encoding_message_size / max_decoding_message_size). All other services use the tonic default 4 MB decode limit. Plan large vector batches accordingly, and put VectorIndexService behind a trusted network boundary if you need to restrict access to it.

Authentication and authorization

Authentication is off by default. Pass --auth to turn it on. The auth behaviour is implemented in sochdb-grpc/src/auth_interceptor.rs and sochdb-grpc/src/security.rs. For the full security model — JWT validation, API key hashing, TLS/mTLS, and Kubernetes secrets — see the Security guide.

Presenting credentials

Clients pass credentials as gRPC metadata headers:

  • authorization: Bearer <token> — preferred. With --auth, the token is validated as a JWT (HS256). JWTs are validation-only: SochDB does not mint tokens — they must be issued by your IdP or caller.
  • x-api-key: <key> — fallback. Internally rewritten to a Bearer header.

The interceptor pipeline is: authenticate → check rate limit → inject the resolved Principal into the request extensions for downstream handlers.

import grpc

# Authenticated channel using a JWT bearer token
channel = grpc.insecure_channel("localhost:50051")
metadata = [("authorization", "Bearer <your-jwt>")]
# ... or, with an API key:
metadata = [("x-api-key", "<your-api-key>")]
JWT vs. API key precedence

When --auth is enabled, both JWT and API-key auth are turned on, and all Bearer tokens are routed to JWT validation. A bare --api-key is only reachable through the x-api-key header path. Keep JWT and API-key clients on the header that matches your intent.

RBAC roles

Authorization is capability-based. The built-in roles and their capabilities are:

RoleCapabilities
OwnerAdmin, Read, Write, ManageCollections, ManageIndexes, ViewMetrics, ManageBackups, ManageUsers
EditorRead, Write, ManageCollections, ManageIndexes
ViewerRead, ViewMetrics

A Custom { name, capabilities } role is also supported. The Admin capability acts as a wildcard. Roles can be carried in the JWT role/capabilities claims or bound server-side via per-namespace role bindings (scoped Global, Namespace, or Collection).

When --auth is not passed, the interceptor runs in pass-through mode and every request resolves to an anonymous principal with Read, Write, and ManageCollections capabilities.

Rate limiting

The default rate limit is 1000 requests/second with a burst of 100 per tenant, enforced inside the auth interceptor. Audit logging is on by default.

TLS and mTLS

TLS is enabled when both --tls-cert and --tls-key are set. Adding --tls-ca additionally enables mTLS client-certificate verification. Certificates can be hot-reloaded (mtime-based). See the Security guide for details and the grpcs:// client scheme.

Change-data-capture subscriptions

SubscriptionService exposes SochDB's WAL-derived CDC stream over gRPC (sochdb-grpc/src/subscription_server.rs).

service SubscriptionService {
rpc Subscribe(SubscribeRequest) returns (stream SubscribeEvent);
rpc WatchKey(WatchKeyRequest) returns (stream WatchKeyEvent);
rpc ListSubscriptions(ListSubscriptionsRequest) returns (ListSubscriptionsResponse);
rpc CancelSubscription(CancelSubscriptionRequest) returns (CancelSubscriptionResponse);
}

A SubscribeRequest carries a namespace, a list of tables, a list of operations (insert/update/delete/schema-change), a start_sequence (0 means "from latest", a value greater than 0 resumes from that CDC sequence), an optional where_predicate, and a batch_size (default 64).

where_predicate is accepted but not yet enforced

The streaming subscription handler applies table filtering and operation-type filtering, but it does not currently read or apply the where_predicate field. SQL-WHERE predicate filtering is parsed into the request but not enforced in the stream loop yet — treat it as accepted-but-not-yet-active.

For the full CDC model (event shape, sequence semantics, resume, and overrun handling), see CDC Subscriptions.

VectorIndexService reference

VectorIndexService is optimized for high-throughput embedding ingestion and k-NN search. It wraps the sochdb-index crate's HNSW implementation.

HNSW configuration

When creating an index you can tune the HNSW (Hierarchical Navigable Small World) parameters. The values below are the server-side defaults; the CreateIndex path falls back to them when a proto field is left at 0.

ParameterDefaultImpact
dimensionrequiredVector size (e.g. 1536 for OpenAI, 768 for BERT)
max_connections (M)32Max edges per node. Higher = better recall, slower build/search
ef_construction256Candidate-list size during build. Higher = better index quality, slower build
ef_search500Candidate-list size during search. Higher = better recall, slower query
metricCosineDistance metric: L2, Cosine, DotProduct
No dimension-based ef_search split

The core engine uses a single ef_search default of 500 regardless of dimension. The dimension-aware logic that does exist is the brute-force flat-scan threshold (≤128D: 10000 vectors, ≤384D: 4000, otherwise 1000), below which a linear scan is used instead of the HNSW graph.

Index management

CreateIndex — initializes a new HNSW graph.

message CreateIndexRequest {
string name = 1;
uint32 dimension = 2;
HnswConfig config = 3;
DistanceMetric metric = 4;
}

DropIndex — removes an index from memory.

Data ingestion

InsertBatch (recommended) — atomic batch insertion using a flat layout for zero-copy deserialization in Rust. The most efficient way to load data.

message InsertBatchRequest {
string index_name = 1;
repeated uint64 ids = 2;
repeated float vectors = 3; // Flat array: [v1_0, v1_1... v2_0...]
}

InsertStream — long-running client-streaming RPC for continuous ingestion.

message InsertStreamRequest {
string index_name = 1; // Only needed in the first message
uint64 id = 2;
repeated float vector = 3;
}

Search — standard k-nearest-neighbors search.

message SearchRequest {
string index_name = 1;
repeated float query = 2;
uint32 k = 3;
uint32 ef = 4; // Optional per-query override; defaults to ef_search from config
}

SearchBatch — execute multiple queries in parallel.

Operations

GetStats — returns internal graph statistics useful for debugging: num_vectors (total count), max_layer (height of the HNSW graph), and avg_connections (graph connectivity density).

HealthCheck — service-level health check for load balancers (distinct from the global tonic_health service).

Client usage example (Python)

import grpc
from sochdb_pb2 import CreateIndexRequest, SearchRequest, HnswConfig
from sochdb_pb2_grpc import VectorIndexServiceStub

# 1. Connect (VectorIndexService has no auth interceptor)
channel = grpc.insecure_channel("localhost:50051")
stub = VectorIndexServiceStub(channel)

# 2. Create an index
stub.CreateIndex(CreateIndexRequest(
name="prod_vectors",
dimension=768,
metric=2, # DISTANCE_METRIC_COSINE (1 = L2, 3 = DOT_PRODUCT)
config=HnswConfig(max_connections=32, ef_construction=256),
))

# 3. Search
response = stub.Search(SearchRequest(
index_name="prod_vectors",
query=[0.1, 0.2], # 768 floats
k=5,
))

for match in response.results:
print(f"Doc {match.id}: Score {match.distance}")
Authenticated services

The example above targets VectorIndexService, which is unauthenticated. For the other 11 services, attach an authorization: Bearer <token> or x-api-key header to every call when the server runs with --auth.