Skip to main content

SochDB Node.js API Reference

Complete reference for the SochDB Node.js / TypeScript SDK.

  • Package: @sochdb/sochdb
  • Version: 0.5.3
  • License: Apache-2.0 (the language SDKs are Apache-2.0; the core Rust engine is AGPL-3.0-or-later with commercial licensing available)
  • Node engine: >=18.0.0
  • Dependencies: @grpc/grpc-js, @grpc/proto-loader, koffi (FFI), uuid; optional posthog-node (analytics)

Installation

npm install @sochdb/sochdb

The package ships three bin scripts: sochdb-server, sochdb-bulk, and sochdb-grpc-server.

Embedded by default

The public surface is centered on an embedded, in-process database backed by a native library loaded through koffi. Remote access is available through the gRPC client (SochDBClient) and the IPC client (IpcClient). There is no routing module in this SDK.

Quick start

import { EmbeddedDatabase } from '@sochdb/sochdb';

// open() is SYNCHRONOUS — it returns an EmbeddedDatabase, not a Promise.
const db = EmbeddedDatabase.open('./my.sochdb');

await db.put(Buffer.from('greeting'), Buffer.from('hello'));
const value = await db.get(Buffer.from('greeting'));
console.log(value?.toString()); // "hello"

db.close();
open() is synchronous

EmbeddedDatabase.open() returns an EmbeddedDatabase directly — it is not a Promise. You may see await EmbeddedDatabase.open(...) in older README snippets; awaiting a non-Promise is harmless but misleading. Do not rely on open() being async.

Exports

The top-level module (@sochdb/sochdb) re-exports the following public surface:

  • Database: EmbeddedDatabase (also exported as Database), EmbeddedDatabaseConfig, open, openConcurrent
  • Transactions: EmbeddedTransaction
  • Clients: SochDBClient (also exported as GrpcClient), IpcClient
  • Vector search: HnswIndex, HnswConfig, HnswBindings, SearchResult (re-exported as HnswSearchResult)
  • Namespaces / collections: Namespace, Collection, DistanceMetric, and their config/result types
  • Query builder: Query
  • Memory: ExtractionPipeline, Consolidator, HybridRetriever, AllowedSet
  • Queue: PriorityQueue, createQueue, TaskState
  • Semantic cache: SemanticCache
  • Context builder: ContextQueryBuilder, createContextBuilder, ContextOutputFormat, TruncationStrategy
  • Studio: StudioClient, StudioAPIError
  • MCP: McpServer, McpClient, McpError, MCP_ERROR_CODES
  • Format: WireFormat, ContextFormat, CanonicalFormat, FormatCapabilities, FormatConversionError
  • Errors: ErrorCode, SochDBError, and the typed error subclasses
  • Version: VERSION ('0.5.3')
Two Database classes — only one is public

A separate IPC-based Database class exists internally (src/database.ts, with its own async open(), Transaction, and SQL query()/execute()), but it is not re-exported from the package entry point. Every public Database.* usage resolves to EmbeddedDatabase. Likewise the SQL engine classes SQLParser/SQLExecutor are internal and not re-exported.

EmbeddedDatabase

The primary class. import { EmbeddedDatabase } from '@sochdb/sochdb' (or use the Database alias).

Opening a database

import { EmbeddedDatabase, open, openConcurrent } from '@sochdb/sochdb';

// Static factory (sync)
const db = EmbeddedDatabase.open('./data.sochdb');

// Top-level convenience functions
const db2 = open('./data.sochdb'); // alias for EmbeddedDatabase.open
const db3 = openConcurrent('./data.sochdb', { fallbackToStandard: true });
MethodSignatureNotes
EmbeddedDatabase.open(path: string, config?: EmbeddedDatabaseConfig) => EmbeddedDatabaseSynchronous
EmbeddedDatabase.openConcurrent(path: string, options?: { fallbackToStandard?: boolean }) => EmbeddedDatabaseMulti-process; throws if the native library is older than v0.4.8 unless fallbackToStandard is set
EmbeddedDatabase.isConcurrentModeAvailable() => boolean
open (top-level)(path: string, config?: EmbeddedDatabaseConfig) => EmbeddedDatabaseAlias for EmbeddedDatabase.open
openConcurrent (top-level)(path: string, options?: { fallbackToStandard?: boolean }) => EmbeddedDatabase

Getters: isConcurrent: boolean, isConcurrentFallback: boolean.

EmbeddedDatabaseConfig

interface EmbeddedDatabaseConfig {
walEnabled?: boolean;
syncMode?: 'full' | 'normal' | 'off';
memtableSizeBytes?: number;
groupCommit?: boolean;
indexPolicy?: 'write_optimized' | 'balanced' | 'scan_optimized' | 'append_only';
}

Key-value operations

EmbeddedDatabase requires Buffer keys and values.

await db.put(Buffer.from('user:1'), Buffer.from(JSON.stringify({ name: 'Ada' })));

const raw = await db.get(Buffer.from('user:1')); // Buffer | null
await db.delete(Buffer.from('user:1'));

// Path-addressed helpers (string paths, Buffer values)
await db.putPath('users/1/name', Buffer.from('Ada'));
const name = await db.getPath('users/1/name'); // Buffer | null
MethodSignature
put(key: Buffer, value: Buffer) => Promise<void>
get(key: Buffer) => Promise<Buffer | null>
delete(key: Buffer) => Promise<void>
putPath(path: string, value: Buffer) => Promise<void>
getPath(path: string) => Promise<Buffer | null>

These top-level KV calls are wrapped in an auto-transaction.

scanPrefix

scanPrefix returns an AsyncGenerator<[Buffer, Buffer]>. The underlying transaction commits when the generator completes and aborts on error. The native iterator frees memory via sochdb_free_bytes and is closed via sochdb_iterator_close in a finally block.

for await (const [key, value] of db.scanPrefix(Buffer.from('user:'))) {
console.log(key.toString(), '=>', value.toString());
}

Checkpoint, stats, close

const lsn = await db.checkpoint(); // Promise<bigint> (returns LSN)

const s = await db.stats();
// {
// memtableSizeBytes: bigint,
// walSizeBytes: bigint,
// activeTransactions: number,
// minActiveSnapshot: bigint,
// lastCheckpointLsn: bigint
// }

db.close(); // void
checkpoint() returns a bigint

EmbeddedDatabase.checkpoint() returns Promise<bigint> (the LSN), and IpcClient.beginTransaction() returns Promise<bigint> (the transaction id). These are the only bigint-returning members — notably, commit() does not return a bigint (see below).

Transactions

Manual transactions

EmbeddedDatabase.transaction() returns an EmbeddedTransaction synchronously.

const txn = db.transaction();
try {
txn.put(Buffer.from('a'), Buffer.from('1'));
txn.put(Buffer.from('b'), Buffer.from('2'));
await txn.commit(); // Promise<void>
} catch (err) {
await txn.abort(); // Promise<void>
throw err;
}
commit() returns Promise<void>

EmbeddedTransaction.commit() returns Promise<void>, not Promise<bigint>. On failure it throws a TransactionError when the native error_code !== 0 (-2 indicates a serializable-snapshot isolation conflict).

EmbeddedTransaction methods:

MethodSignature
put(key: Buffer, value: Buffer) => void
get(key: Buffer) => Buffer | null
delete(key: Buffer) => void
putPath(path: string, value: Buffer) => void
getPath(path: string) => Buffer | null
scanPrefix(prefix: Buffer) => AsyncGenerator<[Buffer, Buffer]>
commit() => Promise<void>
abort() => Promise<void>

withTransaction

withTransaction runs a callback inside a transaction, committing on success and aborting on error.

const total = await db.withTransaction(async (txn) => {
txn.put(Buffer.from('counter'), Buffer.from('1'));
const v = txn.get(Buffer.from('counter'));
return Number(v?.toString() ?? '0');
});
withTransaction<T>(fn: (txn: EmbeddedTransaction) => Promise<T>): Promise<T>

Vector search (HNSW)

import { HnswIndex, HnswConfig } from '@sochdb/sochdb'.

import { HnswIndex } from '@sochdb/sochdb';

const index = new HnswIndex({ dimension: 768 });

index.insert('doc-1', vector768);
index.insertBatch(['doc-2', 'doc-3'], [vecA, vecB]);

const results = index.search(queryVector, 10); // SearchResult[]
const fast = index.search(queryVector, 10, true); // fast mode
const ultra = index.searchUltra(queryVector, 10);

index.close();

HnswConfig

interface HnswConfig {
dimension: number;
maxConnections?: number; // default 32 (engine HnswConfig::default() M=32)
efConstruction?: number; // default 256
efSearch?: number; // default 100 in the HnswIndex constructor
}

These defaults mirror the engine's HnswConfig::default() (M=32, ef_construction=256), with m0=64 and F32 precision inherited from the core. On Cohere-1M (768-dimension, cosine) this configuration reaches roughly 0.972 recall@10.

SearchResult

interface SearchResult {
id: string;
distance: number;
}
MethodSignature
insert(id: string, vector: number[]) => void
insertBatch(ids: string[], vectors: number[][]) => void
search(query: number[], k: number, fast?: boolean) => SearchResult[]
searchUltra(query: number[], k: number) => SearchResult[]
buildFlatCache() => void
close() => void

Getters: length, dimension. Get/set: efSearch.

String IDs are hashed internally

HNSW string IDs are hashed to a numeric representation internally. idToString only recovers the low 64 bits, so the string round-trip can be lossy for IDs that collide on those bits.

Namespaces and collections

import { Namespace, Collection, DistanceMetric } from '@sochdb/sochdb'.

On EmbeddedDatabase: createNamespace(name, config?), namespace(name), getOrCreateNamespace(name, config?), deleteNamespace(name) => boolean, listNamespaces() => string[].

import { EmbeddedDatabase, DistanceMetric } from '@sochdb/sochdb';

const db = EmbeddedDatabase.open('./vectors.sochdb');
const ns = db.getOrCreateNamespace('app');

const docs = ns.getOrCreateCollection({
name: 'documents',
dimension: 768,
metric: DistanceMetric.Cosine,
indexed: true,
});

const id = docs.insert(vector768, { title: 'Intro' });
docs.insertMany([vecA, vecB], [{ t: 'a' }, { t: 'b' }]);

const hits = docs.search({ queryVector: vector768, k: 5, includeMetadata: true });

Namespace

MethodSignature
createCollection(config: CollectionConfig) => Collection
collection(name: string) => Collection
getOrCreateCollection(config: CollectionConfig) => Collection
deleteCollection(name: string) => boolean
listCollections() => string[]
getName() => string
getConfig() => NamespaceConfig

Collection

MethodSignature
insert(vector: number[], metadata?, id?) => string
insertMany(vectors, metadatas?, ids?) => string[] (native HNSW batch fast path)
search(request: SearchRequest) => SearchResult[]
get(id: string) => ...
delete(id: string) => boolean
count() => number
rebuildIndex() => number
setEfSearch(n: number) => void (default efSearch=500)

Getter: isIndexReady: boolean.

Config and result types

enum DistanceMetric {
Cosine = 'cosine',
Euclidean = 'euclidean',
DotProduct = 'dot',
}

interface CollectionConfig {
name: string;
dimension?: number; // default 384
metric?: DistanceMetric;
indexed?: boolean;
hnswM?: number; // default 32
hnswEfConstruction?: number; // default 256
metadata?: Record<string, unknown>;
}

interface NamespaceConfig {
name: string;
displayName?: string;
labels?: Record<string, string>;
readOnly?: boolean;
}

interface SearchRequest {
queryVector: number[];
k: number;
filter?: unknown;
includeMetadata?: boolean;
}

interface SearchResult { // re-exported as NamespaceSearchResult
id: string;
score: number;
vector?: number[];
metadata?: Record<string, unknown>;
}

Remote clients

SochDBClient (gRPC)

import { SochDBClient } from '@sochdb/sochdb' (also exported as GrpcClient). All methods are async.

import { SochDBClient } from '@sochdb/sochdb';

const client = new SochDBClient({ address: 'localhost:50051' });

await client.createIndex('docs', 768, { metric: 'cosine', m: 32, efConstruction: 256 });
await client.insertVectors('docs', [1, 2], [vecA, vecB]);
const results = await client.search('docs', queryVector, 10, 50); // SearchResult[]

await client.put('key', 'value', 'default');
const v = await client.get('key', 'default');
await client.delete('key', 'default');

await client.close();
interface SochDBClientOptions {
address?: string; // default 'localhost:50051'
secure?: boolean;
protoPath?: string;
}

Selected methods (all async):

MethodNotes
createIndex(name, dimension, { metric?, m?, efConstruction? })defaults m=32, efConstruction=256; returns boolean
insertVectors(indexName, ids: number[], vectors: number[][])returns number
search(indexName, query, k=10, ef=50)returns SearchResult[] ({ id: number, distance: number })
createCollection(name, { dimension, namespace?, metric? })
addDocuments(...), searchCollection(...)document collections
addNode, addEdge, traversegraph
cacheGet, cachePutcache
startTrace, startSpan, endSpantracing
get(key, namespace='default'), put(...), delete(key, namespace='default')KV; delete returns boolean
close()

IpcClient (Unix domain sockets)

import { IpcClient } from '@sochdb/sochdb'.

Not available on Windows

IpcClient communicates over Unix domain sockets and is not available on Windows.

import { IpcClient } from '@sochdb/sochdb';

const client = await IpcClient.connect('/tmp/sochdb.sock');

await client.put('key', 'value');
const value = await client.get('key');

const txnId = await client.beginTransaction(); // Promise<bigint>
await client.commitTransaction(txnId);

const ok = await client.ping(); // Promise<boolean>
await client.close();

Methods: get/put/delete, getPath/putPath, query(prefix, { limit, offset, columns }), scan(prefix) => Array<{ key, value }>, beginTransaction() => Promise<bigint>, commitTransaction(txnId), abortTransaction(txnId), checkpoint(), stats(), ping() => Promise<boolean>, close().

Query builder

The exported Query builder (import { Query } from '@sochdb/sochdb') runs a path-prefix query over an IpcClient (this is distinct from the internal SQL engine, which is not exported).

const result = await new Query(client, 'users/')
.select(['name', 'email'])
.limit(20)
.offset(0)
.toList(); // QueryResult[] ({ [key: string]: unknown })

Methods: limit(n), offset(n), select(cols[]), execute() => Promise<string> (TOON), toList() => Promise<QueryResult[]>, first(), count().

Memory system

import { ExtractionPipeline, Consolidator, HybridRetriever, AllowedSet } from '@sochdb/sochdb'. (Added in v0.4.2.) These provide an LLM-driven entity/relation/assertion pipeline, fact consolidation, and hybrid BM25-plus-vector retrieval.

import { ExtractionPipeline, Consolidator, HybridRetriever, AllowedSet } from '@sochdb/sochdb';

const pipeline = new ExtractionPipeline(db, 'memory');
const extraction = await pipeline.extractAndCommit(text, myExtractor);

const consolidator = new Consolidator(db, 'memory');
await consolidator.add(rawAssertion);
const merged = await consolidator.consolidate(); // number of facts

const retriever = HybridRetriever.fromDatabase(db, 'memory', 'documents', {
k: 10,
alpha: 0.5,
enableRerank: false,
rerankK: 100,
});
const response = await retriever.retrieve(queryText, queryVector, AllowedSet.allowAll(), 10);
  • ExtractionPipelinenew ExtractionPipeline(db, namespace, schema?); extract(text, extractor), extractAndCommit(...), commit(result), getEntities(), getRelations(), getAssertions(). ExtractorFunction = (text) => Promise<{ ... }>.
  • Consolidatornew Consolidator(db, namespace, config?); add(assertion), addWithContradiction(newAssertion, contradicts), consolidate() => number, getCanonicalFacts(), explain(factId).
  • HybridRetrievernew HybridRetriever(db, namespace, collection, config?) or HybridRetriever.fromDatabase(...); indexDocuments(docs), retrieve(queryText, queryVector, allowed, k?), explain(...). RetrievalConfig defaults: k=10, alpha=0.5, enableRerank=false, rerankK=100.
  • AllowedSet — abstract pre-filter with factories AllowedSet.fromIds(ids), .fromNamespace(ns), .fromFilter(fn), .allowAll(); instance method contains(id, metadata?).

Exported types: Entity, Relation, Assertion, RawAssertion, CanonicalFact, ExtractionResult, ExtractionSchema, ConsolidationConfig, RetrievalConfig, RetrievalResult, RetrievalResponse.

Priority queue

import { PriorityQueue, createQueue, TaskState } from '@sochdb/sochdb'. (Added in v0.4.1.) An O(log N) priority queue with ordered keys, visibility timeout, and dead-letter support.

import { createQueue } from '@sochdb/sochdb';

const queue = createQueue(db, 'jobs', { visibilityTimeout: 30000, maxRetries: 3 });

const taskId = queue.enqueue(5, Buffer.from(JSON.stringify({ job: 'resize' })));
const task = queue.dequeue('worker-1'); // Task | null
if (task) {
queue.ack(task.id); // or queue.nack(task.id)
}

const stats = queue.stats();
  • PriorityQueuePriorityQueue.fromDatabase(db, name, config?), PriorityQueue.fromClient(client, name, config?); enqueue(priority, payload, metadata?) => string, dequeue(workerId) => Task | null, ack(taskId), nack(taskId), stats() => QueueStats, purge() => number.
  • createQueue(db, name, config?) — top-level helper returning a PriorityQueue.
enum TaskState { PENDING, CLAIMED, COMPLETED, DEAD_LETTERED }

interface QueueConfig {
name: string;
visibilityTimeout?: number; // default 30000 ms
maxRetries?: number; // default 3
deadLetterQueue?: string;
}

Types: QueueKey, Task, QueueStats.

Binary-safe key parsing

The queue decodes its keys with positional binary parsing because keys may legitimately contain the 0x2F byte (/); it avoids an unsafe split('/').

Semantic cache

import { SemanticCache } from '@sochdb/sochdb'. (Added in v0.4.1.) Caches LLM responses keyed by embedding similarity (cosine).

import { SemanticCache } from '@sochdb/sochdb';

const cache = new SemanticCache(db, 'llm-responses');

cache.put('prompt-1', 'cached answer', embedding, 3600, { model: 'opus' });

const hit = cache.get(queryEmbedding, 0.85); // CacheHit | null
if (hit) {
console.log(hit.value, 'score:', hit.score);
}

const removed = cache.purgeExpired(); // number

Methods: new SemanticCache(db, cacheName); put(key, value, embedding, ttlSeconds=0, metadata?), get(queryEmbedding, threshold=0.85) => CacheHit | null, delete(key), clear() => number, stats() => CacheStats, purgeExpired() => number.

Types: CacheEntry, CacheHit (extends CacheEntry with score), CacheStats { count, hits, misses, hitRate, memoryUsage }.

Context builder

import { ContextQueryBuilder, createContextBuilder } from '@sochdb/sochdb'. (Added in v0.4.1.) A fluent, token-aware, priority-based assembler for LLM context windows.

import { createContextBuilder, ContextOutputFormat, TruncationStrategy } from '@sochdb/sochdb';

const result = createContextBuilder()
.forSession('session-123')
.withBudget(4096)
.setFormat(ContextOutputFormat.MARKDOWN)
.setTruncation(TruncationStrategy.TAIL_DROP)
.literal('system', 100, 'You are a helpful assistant.')
.last(5, 'messages')
.search('documents', queryEmbedding, 3)
.done()
.execute(); // ContextResult { text, tokenCount, sections[] }

Builder methods: forSession(id), withBudget(tokens) (default 4096), setFormat(ContextOutputFormat), setTruncation(TruncationStrategy), literal(name, priority, text), section(name, priority), get(path), last(n, table), whereEq(field, value), search(collection, embedding, k), sql(query), done(), execute().

enum ContextOutputFormat { TOON, JSON, MARKDOWN }
enum TruncationStrategy { TAIL_DROP, HEAD_DROP, PROPORTIONAL }

Graph overlay

The graph overlay is a thin KV wrapper exposed directly on EmbeddedDatabase. Keys are stored under _graph/{ns}/nodes/... and _graph/{ns}/edges/....

await db.addNode('social', 'user:1', 'User', { name: 'Ada' });
await db.addEdge('social', 'user:1', 'FOLLOWS', 'user:2');

const sub = await db.traverse('social', 'user:1', 10, 'bfs'); // { nodes, edges }
MethodSignature
addNode(namespace, nodeId, nodeType, properties?)
addEdge(namespace, fromId, edgeType, toId, properties?)
traverse(namespace, startNode, maxDepth=10, order: 'bfs' | 'dfs' = 'bfs') => { nodes, edges }

Policy

import { PolicyService } from '@sochdb/sochdb'. (Added in v0.4.3.) Namespace-level ACL and governance over an EmbeddedDatabase.

import { PolicyService } from '@sochdb/sochdb';

const policy = new PolicyService(db, { enableAudit: true });

policy.createNamespacePolicy(/* ... */);
policy.addRule('app', rule);
policy.grantAccess(grant);

const decision = policy.evaluate(request); // PolicyEvaluation
const log = policy.getAuditLog();

Methods: new PolicyService(db, { enableAudit? }); createNamespacePolicy, getNamespacePolicy => NamespacePolicy | null, updateNamespacePolicy, deleteNamespacePolicy => boolean, addRule(ns, rule), removeRule(ns, ruleId) => boolean, evaluate(req) => PolicyEvaluation, grantAccess(grant), revokeAccess(ns, principal) => boolean, hasPermission(...), listGrants(ns) => NamespaceGrant[], getAuditLog(options?), clearCache().

Types: PolicyRule, PolicyCondition, PolicyEvaluation, NamespacePolicy, NamespaceGrant, NamespacePermission, PolicyAction, PolicyRequest, PolicySet, PolicyAuditEntry.

MCP

import { McpServer, McpClient } from '@sochdb/sochdb'. (Added in v0.4.3.) Exposes the database as MCP tools, resources, and prompts to LLM agents.

import { McpServer } from '@sochdb/sochdb';

const server = new McpServer(db, mcpServerConfig);
server.registerTool(/* ... */);
const tools = server.listTools();
const result = await server.callTool(toolCall); // McpToolResult
  • McpServernew McpServer(db, config); registerTool(...), unregisterTool(name) => boolean, listTools(), callTool(call) => McpToolResult, registerPrompt(prompt), listPrompts(), getPrompt(name, args?), listResources(), readResource(uri), getServerInfo().
  • McpClientnew McpClient(config); connect(), disconnect(), isConnected(), listTools(), callTool(name, args), listResources(), readResource(uri), listPrompts(), getPrompt(name, args?).
  • McpError(message, code, data?) and the MCP_ERROR_CODES constant.

Types: McpTool, McpToolCall, McpToolResult, McpResource, McpResourceContent, McpPrompt, McpPromptArgument, McpPromptMessage, McpServerCapabilities, McpServerConfig, McpClientConfig, McpTransport.

MCP tool names use underscores

When the engine exposes its built-in MCP tools, the names use underscores — for example sochdb_query, sochdb_get, sochdb_put, sochdb_context_query, memory_search_episodes, sochdb_grep. Any dot-separated tool catalog you may find in older mcp.json/README material is stale.

Studio

import { StudioClient } from '@sochdb/sochdb'. An HTTP client (Bearer auth) for the hosted Studio backend.

import { StudioClient } from '@sochdb/sochdb';

const studio = new StudioClient({ baseUrl: 'https://studio.example.com', apiKey: 'sk_...' });

await studio.health();
const ingest = await studio.ingestEvents(events, { source: 'app' }); // { ok, ingested, eventIds }

Methods: new StudioClient({ baseUrl, apiKey? }); health() => StudioHealthResponse, ingestEvents(events, { source?, apiKey? }) => StudioIngestResult.

Error: StudioAPIError(message, statusCode?). Types: StudioClientOptions, StudioEvent, StudioHealthResponse, StudioIngestResult { ok, ingested, eventIds }.

Format utilities

import { WireFormat, ContextFormat, CanonicalFormat, FormatCapabilities } from '@sochdb/sochdb'.

enum WireFormat { TOON = 'toon', JSON = 'json', COLUMNAR = 'columnar' }
enum ContextFormat { TOON, JSON, MARKDOWN }
// CanonicalFormat also includes TOON
  • WireFormat.fromString(...), ContextFormat.fromString(...).
  • FormatCapabilities class — e.g. FormatCapabilities.wireToContext(wire).
  • FormatConversionError extends Error.

Static serialization helpers exist on EmbeddedDatabase: toToon(table, records, fields?), toJson(table, records, fields?, compact=true), fromJson(jsonStr).

Error handling

import { SochDBError, ErrorCode } from '@sochdb/sochdb'.

import { SochDBError, ErrorCode, TransactionError } from '@sochdb/sochdb';

try {
await db.withTransaction(async (txn) => {
txn.put(Buffer.from('k'), Buffer.from('v'));
// ...
});
} catch (err) {
if (err instanceof TransactionError) {
console.error('txn failed:', err.code, err.remediation);
} else if (err instanceof SochDBError) {
console.error(err.code, err.message);
}
}

ErrorCode

CodeValueCodeValue
CONNECTION_FAILED1001INTERNAL_ERROR9001
CONNECTION_TIMEOUT1002STORAGE_ERROR9003
CONNECTION_CLOSED1003DATABASE_LOCKED10001
PROTOCOL_ERROR1004LOCK_TIMEOUT10002
TRANSACTION_ABORTED2001EPOCH_MISMATCH10003
TRANSACTION_CONFLICT2002SPLIT_BRAIN10004
STALE_LOCK10005

Error classes

  • SochDBError(message, code = INTERNAL_ERROR, remediation?) — base class with .code and .remediation.
  • ConnectionError, TransactionError, ProtocolError, DatabaseError.
  • Lock errors (v0.4.1): LockError, DatabaseLockedError(path, holderPid?) (.path, .holderPid), LockTimeoutError(path, timeoutSecs), EpochMismatchError(expected, actual), SplitBrainError(message?).
  • Namespace / collection errors: NamespaceNotFoundError, NamespaceExistsError, CollectionNotFoundError, CollectionExistsError (all extend SochDBError).
  • Service errors: StudioAPIError, McpError.

Platform notes

Unix-socket features are POSIX-only

The IPC client and the internal embedded-server manager use Unix domain sockets. IpcClient is not available on Windows, and the internal server manager throws on Windows. The embedded EmbeddedDatabase (FFI via koffi) and the gRPC SochDBClient are cross-platform.

See also


For the latest documentation, see sochdb.dev