brainy/docs/api/README.md
David Snelling c2b73d4564 docs(8.0): export/import guide + api/README portable backup section
- New docs/guides/export-and-import.md (public) for the 8.0 surface:
  brain.export()/import(), Db composition (asOf/with time-travel + what-if export),
  selectors, options, BackupData v1 format, cross-version (7.x→8.0), VFS, and the
  generations/persist distinction. Documents only the implemented surface.
- api/README "Export & Import (portable) + Snapshots (native)": adds the portable
  brain.export()/import() round-trip alongside the native persist()/asOf() snapshot.
2026-06-17 11:24:57 -07:00

2222 lines
71 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: API Reference
slug: api/reference
public: true
category: api
template: api
order: 1
description: Complete API reference for all Brainy methods — add, find, relate, update, delete, batch operations, the Db API (transactions, snapshots, time travel), VFS, neural API, and more.
next:
- getting-started/quick-start
- guides/find-system
---
# 🧠 Brainy API Reference
> **Complete API documentation for Brainy**
> Zero Configuration • Triple Intelligence • Database as a Value • Atomic Transactions • Time Travel
**Updated:** 2026-06-11
**All APIs verified against actual code**
---
## Quick Start
```typescript
import { Brainy, NounType, VerbType } from '@soulcraft/brainy'
const brain = new Brainy() // Zero config!
await brain.init() // VFS auto-initialized!
// Add data (text auto-embeds!)
const id = await brain.add({
data: 'The future of AI is here',
type: NounType.Concept,
metadata: { category: 'technology' }
})
// Search with Triple Intelligence
const results = await brain.find({
query: 'artificial intelligence',
where: { year: { greaterThan: 2020 } },
connected: { from: id, depth: 2 }
})
// Pin the current state as an immutable value
const db = brain.now()
// Commit an atomic multi-write batch (all-or-nothing)
await brain.transact([
{ op: 'update', id, metadata: { category: 'AI' } }
], { meta: { author: 'docs-example' } })
await db.get(id) // still sees the pre-transaction state — snapshot isolation
await db.release()
// Time travel: query any past state
const yesterday = await brain.asOf(new Date(Date.now() - 86_400_000))
```
---
## Core Concepts
### 🧬 Entities (Nouns)
Semantic vectors with metadata and relationships - the fundamental data unit in Brainy.
### 🔗 Relationships (Verbs)
Typed connections between entities with optional `data` and `metadata` - building knowledge graphs.
### 📊 Data vs Metadata
- **`data`**: Content embedded into vectors. Searchable via **semantic similarity** (vector index) and **hybrid text+semantic** search. NOT queryable via `where` filters.
- **`metadata`**: Structured fields indexed by MetadataIndex. Queryable via `where` filters in `find()`.
See **[Data Model](../DATA_MODEL.md)** for the full explanation.
### 🧠 Triple Intelligence
Vector search + Graph traversal + Metadata filtering in one unified query.
### 🧊 Database Values (Db)
The whole store pinned as an immutable value — snapshot isolation, atomic `transact()` batches, time travel with `asOf()`, instant hard-link snapshots with `persist()`. See the [consistency model](../concepts/consistency-model.md).
---
## Table of Contents
- [Core CRUD Operations](#core-crud-operations)
- [Search & Query](#search--query)
- [Aggregation Engine](#aggregation-engine)
- [Relationships](#relationships)
- [Batch Operations](#batch-operations)
- [Database Values & Time Travel (Db API)](#database-values--time-travel-db-api)
- [Virtual Filesystem (VFS)](#virtual-filesystem-vfs)
- [Neural API](#neural-api)
- [Import & Export](#import--export)
- [Configuration](#configuration)
- [Storage Adapters](#storage-adapters)
- [Utility Methods](#utility-methods)
- [Embedding & Analysis APIs](#embedding--analysis-apis)
- [Type System Reference](#type-system-reference)
---
## Core CRUD Operations
### `add(params)` → `Promise<string>`
Add a single entity to the database.
```typescript
const id = await brain.add({
data: 'JavaScript is a programming language', // Text or pre-computed vector
type: NounType.Concept, // Required: Entity type
subtype: 'language', // Optional: sub-classification
metadata: { // Optional: queryable fields
category: 'programming',
year: 1995
}
})
```
**Parameters:**
- `data`: `string | number[]` - Content to embed (text auto-embeds) or pre-computed vector
- `type`: `NounType` - Entity type (required)
- `subtype?`: `string` - Per-product sub-classification within the NounType (top-level standard field, indexed on the fast path). See [Subtypes & Facets](../guides/subtypes-and-facets.md).
- `metadata?`: `object` - Structured queryable fields (indexed by MetadataIndex, used in `where` filters)
- `id?`: `string` - Custom ID (auto-generated UUID if not provided)
- `vector?`: `number[]` - Pre-computed vector (skips auto-embedding)
- `confidence?`: `number` - Type classification confidence (0-1)
- `weight?`: `number` - Entity importance/salience (0-1)
- `ifAbsent?`: `boolean` - By-ID idempotent insert. When `true` AND a custom `id` is supplied AND an entity with that `id` already exists, returns the existing `id` without writing (no throw, no overwrite). Ignored without `id`. See [guides/optimistic-concurrency](../guides/optimistic-concurrency.md).
> **`data`** is embedded into vectors for semantic search. **`metadata`** is indexed for `where` filters. See [Data Model](../DATA_MODEL.md).
> **Strict-mode tip:** if a vocabulary is registered for your `type` (via `brain.requireSubtype()` or by an SDK that wraps Brainy), you must pass a matching `subtype`. Run `await brain.audit()` to inventory pre-existing gaps before enabling strict mode; see the [migration recipe](../guides/subtypes-and-facets.md#strict-mode-in-practice-for-sdk-style-vocabulary-consumers).
**Returns:** `Promise<string>` - Entity ID
---
### `get(id)` → `Promise<Entity | null>`
Retrieve a single entity by ID.
```typescript
const entity = await brain.get(id)
console.log(entity?.data) // Original data
console.log(entity?.metadata) // Metadata
console.log(entity?.vector) // Embedding vector
```
**Parameters:**
- `id`: `string` - Entity ID
**Returns:** `Promise<Entity | null>` - Entity or null if not found
---
### `update(params)` → `Promise<void>`
Update an existing entity.
```typescript
await brain.update({
id: entityId,
data: 'Updated content', // Optional: new data
subtype: 'archived', // Optional: change sub-classification
metadata: { updated: true } // Optional: new metadata (merges)
})
```
**Parameters:**
- `id`: `string` - Entity ID
- `data?`: `string | number[]` - New data/vector
- `type?`: `NounType` - Change entity type
- `subtype?`: `string` - Change subtype (omit to preserve existing)
- `metadata?`: `object` - Metadata to merge (or replace with `merge: false`)
- `confidence?`: `number` - Update classification confidence
- `weight?`: `number` - Update entity importance
- `ifRev?`: `number` - Optimistic-concurrency check. When provided, the update throws `RevisionConflictError` if the persisted entity's `_rev` no longer equals `ifRev`. See [guides/optimistic-concurrency](../guides/optimistic-concurrency.md).
**Returns:** `Promise<void>`
> **Tip — read-then-CAS.** Every entity returned by `get()` / `find()` / `search()` carries `entity._rev` (a monotonic counter Brainy auto-bumps on every successful `update()`). Pass it back as `ifRev` to make multi-writer coordination safe without an external lock service. Full guide: [guides/optimistic-concurrency](../guides/optimistic-concurrency.md).
---
### `remove(id)` → `Promise<void>`
Remove a single entity (and every relationship where it is source or target).
```typescript
await brain.remove(id)
```
**Parameters:**
- `id`: `string` - Entity ID
**Returns:** `Promise<void>`
---
## Search & Query
### `find(query)` → `Promise<Result[]>`
**Triple Intelligence** - Vector + Graph + Metadata in ONE query.
```typescript
// Simple text search
const results = await brain.find('machine learning')
// Advanced Triple Intelligence query
const results = await brain.find({
query: 'artificial intelligence', // Vector similarity
where: { // Metadata filtering
year: { greaterThan: 2020 },
category: { oneOf: ['AI', 'ML'] }
},
connected: { // Graph traversal
to: conceptId,
depth: 2,
type: VerbType.RelatedTo
},
limit: 10
})
```
**Parameters:**
- `query`: `string | FindParams`
- **Simple:** Just text for vector search
- **Advanced:** Object with vector + graph + metadata filters
**FindParams:**
- `query?`: `string` - Text for semantic + hybrid search (searches `data` via the vector index + text index)
- `type?`: `NounType | NounType[]` - Filter by entity type(s). Alias for `where.noun`.
- `subtype?`: `string | string[]` - Filter by sub-classification (top-level standard field, fast path). Single string for equality, array for set membership.
- `where?`: `object` - Metadata filters. See **[Query Operators](../QUERY_OPERATORS.md)** for all operators.
- `connected?`: `object` - Graph traversal options
- `to?`: `string` - Target entity ID
- `from?`: `string` - Source entity ID
- `via?`: `VerbType | VerbType[]` - Relationship type(s) to traverse
- `type?`: `VerbType | VerbType[]` - Alias for `via`
- `depth?`: `number` - Traversal depth (default: 1)
- `direction?`: `'in' | 'out' | 'both'` - Traversal direction (default: 'both')
- `limit?`: `number` - Max results (default: 10)
- `offset?`: `number` - Skip results
- `orderBy?`: `string` - Field to sort by (e.g., 'createdAt', 'metadata.priority')
- `order?`: `'asc' | 'desc'` - Sort direction (default: 'asc')
- `searchMode?`: `'auto' | 'text' | 'semantic' | 'hybrid'` - Search strategy:
- `'auto'` (default): Zero-config hybrid combining text + semantic search
- `'text'`: Pure keyword/text matching
- `'semantic'`/`'vector'`: Pure vector similarity
- `'hybrid'`: Explicit hybrid mode
- `hybridAlpha?`: `number` - Balance between text (0.0) and semantic (1.0) search. Auto-detected by query length if not specified.
- `excludeVFS?`: `boolean` - Exclude VFS entities from results (default: false)
> **`limit` tip:** Brainy caps `limit` against an auto-configured maximum (based on container/free memory, ~25 KB per result). Above the cap you get a one-time warning per call site; above 2× the cap it throws. To raise the cap, pass `new Brainy({ maxQueryLimit: N })` or `{ reservedQueryMemory: bytes }`. For queries that need ALL matches, paginate with `{ limit, offset }` — that's the only pattern guaranteed to keep working across Brainy versions. See [Query Limits & Pagination](../guides/find-limits.md).
**Returns:** `Promise<Result[]>` - Matching entities with scores
---
### Hybrid Search
Brainy automatically combines text (keyword) and semantic (vector) search for optimal results. No configuration needed.
```typescript
// Zero-config hybrid search (just works)
const results = await brain.find({
query: 'David Smith' // Finds both exact text matches AND semantically similar
})
// Force text-only search (exact keyword matching)
const textResults = await brain.find({
query: 'exact keyword',
searchMode: 'text'
})
// Force semantic-only search (vector similarity)
const semanticResults = await brain.find({
query: 'artificial intelligence concepts',
searchMode: 'semantic'
})
// Custom hybrid weighting (0 = text only, 1 = semantic only)
const customResults = await brain.find({
query: 'David Smith',
hybridAlpha: 0.3 // Favor text matching
})
```
**How it works:**
- Short queries (1-2 words) automatically favor text matching
- Long queries (5+ words) automatically favor semantic search
- Results are combined using Reciprocal Rank Fusion (RRF)
---
### Match Visibility
Search results include detailed match information:
```typescript
const results = await brain.find({ query: 'david the warrior' })
// Each result now includes:
results[0].textMatches // ["david", "warrior"] - exact query words found
results[0].textScore // 0.25 - text match quality (0-1)
results[0].semanticScore // 0.87 - semantic similarity (0-1)
results[0].matchSource // 'both' | 'text' | 'semantic'
```
**Use cases:**
- Highlight exact matches in UI (textMatches)
- Explain why a result ranked high (matchSource)
- Debug search behavior (separate scores)
---
### `highlight(params)` → `Promise<Highlight[]>` ✨
Zero-config highlighting for both exact matches AND semantic concepts.
Handles plain text, rich-text JSON (TipTap, Slate, Lexical, Draft.js, Quill), HTML, and Markdown automatically.
```typescript
// Plain text (works as before)
const highlights = await brain.highlight({
query: "david the warrior",
text: "David Smith is a brave fighter who battles dragons"
})
// [
// { text: "David", score: 1.0, position: [0, 5], matchType: 'text' },
// { text: "fighter", score: 0.78, position: [25, 32], matchType: 'semantic' },
// { text: "battles", score: 0.72, position: [37, 44], matchType: 'semantic' }
// ]
// Rich-text JSON (auto-detected)
const highlights = await brain.highlight({
query: "david the warrior",
text: JSON.stringify(tiptapDocument) // TipTap, Slate, Lexical, Draft.js, Quill
})
// Extracts text from nodes, annotates with contentCategory:
// [
// { text: "David", score: 1.0, matchType: 'text', contentCategory: 'title' },
// { text: "fighter", score: 0.78, matchType: 'semantic', contentCategory: 'content' }
// ]
// HTML input (auto-detected)
const highlights = await brain.highlight({
query: "warrior",
text: "<h1>David the Warrior</h1><p>A brave fighter.</p>"
})
// Custom extractor for proprietary formats
const highlights = await brain.highlight({
query: "function",
text: sourceCode,
contentExtractor: (text) => treeSitterParse(text) // Your custom parser
})
```
**Parameters:**
- `query`: `string` - The search query
- `text`: `string` - Text to highlight (plain text, JSON, HTML, or Markdown)
- `granularity?`: `'word' | 'phrase' | 'sentence'` - Highlight unit (default: 'word')
- `threshold?`: `number` - Min similarity for semantic matches (default: 0.5)
- `contentType?`: `ContentType` - Optional hint: `'plaintext' | 'richtext-json' | 'html' | 'markdown'`. Skips auto-detection when provided.
- `contentExtractor?`: `(text: string) => ExtractedSegment[]` - Custom parser. Bypasses built-in detection entirely.
**Returns:** `Promise<Highlight[]>`
- `text` - The matched text
- `score` - Match score (1.0 for text matches, varies for semantic)
- `position` - [start, end] indices in extracted text
- `matchType` - `'text'` (exact) or `'semantic'` (concept)
- `contentCategory?` - `'title' | 'annotation' | 'content' | 'value' | 'code' | 'structural'` — Role of the source text. Built-in extractors produce `'title'`, `'content'`, `'code'`. All 6 categories are available for custom parsers.
**Supported Rich-Text Formats:**
| Format | Detection | Text nodes |
|--------|-----------|------------|
| TipTap / ProseMirror | `{ type: 'doc', content: [...] }` | `{ type: 'text', text }` |
| Slate.js | `[{ type, children }]` | `{ text }` |
| Lexical | `{ root: { children } }` | `{ type: 'text', text }` |
| Draft.js | `{ blocks: [{ text }] }` | `{ text }` in block |
| Quill Delta | `{ ops: [{ insert }] }` | `{ insert }` |
| HTML | Tags like `<h1>`, `<p>`, `<code>` | Visible text content |
| Markdown | `#` headings, ` ``` ` code blocks | Stripped markup |
**Timeout Protection:**
Semantic matching has a 10-second timeout. If embedding takes too long (e.g., WASM stall), `highlight()` returns text-only matches instead of hanging.
**UI Pattern:**
```typescript
// Style differently based on match type and content category
highlights.forEach(h => {
const style = h.matchType === 'text' ? 'font-weight: bold' : 'background: yellow'
if (h.contentCategory === 'title') { /* render as heading highlight */ }
if (h.contentCategory === 'code') { /* render with code styling */ }
if (h.contentCategory === 'annotation') { /* render as comment/caption */ }
// Apply style from h.position[0] to h.position[1]
})
```
---
### Query Operators
Brainy uses clean, readable operators (BFO — Brainy Field Operators):
| Operator | Description | Example |
|----------|-------------|---------|
| `equals` / `eq` | Exact match | `{age: {equals: 25}}` |
| `notEquals` / `ne` | Not equal | `{status: {notEquals: 'deleted'}}` |
| `greaterThan` / `gt` | Greater than | `{age: {greaterThan: 18}}` |
| `greaterEqual` / `gte` | Greater or equal | `{score: {greaterEqual: 90}}` |
| `lessThan` / `lt` | Less than | `{price: {lessThan: 100}}` |
| `lessEqual` / `lte` | Less or equal | `{rating: {lessEqual: 3}}` |
| `between` | Inclusive range | `{year: {between: [2020, 2025]}}` |
| `oneOf` / `in` | In array | `{color: {oneOf: ['red', 'blue']}}` |
| `noneOf` | Not in array | `{status: {noneOf: ['deleted']}}` |
| `contains` | Array contains value | `{tags: {contains: 'ai'}}` |
| `exists` / `missing` | Field existence | `{email: {exists: true}}` |
| `startsWith` | String prefix | `{name: {startsWith: 'John'}}` |
| `endsWith` | String suffix | `{email: {endsWith: '@gmail.com'}}` |
| `matches` | Pattern match | `{text: {matches: /^[A-Z]/}}` |
| `allOf` | AND combinator | `{allOf: [{active: true}, {role: 'admin'}]}` |
| `anyOf` | OR combinator | `{anyOf: [{role: 'admin'}, {role: 'owner'}]}` |
**[Complete Operator Reference →](../QUERY_OPERATORS.md)** — all operators, aliases, indexed vs in-memory support matrix, and practical examples.
---
## Aggregation Engine
Brainy's aggregation engine maintains **incremental running totals** at write time, delivering O(1) aggregate reads regardless of dataset size. Define aggregates once, and every `add()`, `update()`, and `delete()` automatically updates the running metrics.
### `defineAggregate(definition)` → `void`
Register a named aggregate for incremental computation.
```typescript
brain.defineAggregate({
name: 'monthly_spending',
source: {
type: NounType.Event,
where: { domain: 'financial' } // matches custom metadata fields
},
groupBy: [
'category',
{ field: 'date', window: 'month' } // Time-windowed dimension
],
metrics: {
total: { op: 'sum', field: 'amount' },
count: { op: 'count' },
average: { op: 'avg', field: 'amount' },
highest: { op: 'max', field: 'amount' },
lowest: { op: 'min', field: 'amount' },
spread: { op: 'stddev', field: 'amount' } // Welford's online algorithm
},
materialize: true // Optional: write results as NounType.Measurement entities
})
```
**Parameters:**
| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Unique identifier for this aggregate |
| `source.type` | `NounType \| NounType[]` | Entity types that feed into this aggregate |
| `source.where` | `Record<string, unknown>` | Filter on custom **metadata** fields (matched against the entity's `metadata` bag) |
| `source.service` | `string` | Multi-tenancy filter |
| `groupBy` | `GroupByDimension[]` | Dimensions to group by — plain field names, `{ field, window }` for time bucketing, or `{ field, unnest: true }` for array fields (one contribution per element) |
| `metrics` | `Record<string, AggregateMetricDef>` | Named metrics with `op` (`sum`, `count`, `avg`, `min`, `max`, `stddev`, `variance`, `percentile`, `distinctCount`) and optional `field`. `percentile` additionally requires `p` in `[0, 1]`. |
| `materialize` | `boolean \| object` | Write results as `NounType.Measurement` entities (auto-visible in OData/Sheets/SSE) |
**Time window granularities:** `'hour'`, `'day'`, `'week'`, `'month'`, `'quarter'`, `'year'`, or `{ seconds: number }` for custom intervals.
### `removeAggregate(name)` → `void`
Remove a named aggregate and clean up its state.
```typescript
brain.removeAggregate('monthly_spending')
```
### Querying Aggregates via `find()`
Aggregate results are queried through the standard `find()` method using the `aggregate` parameter:
```typescript
// Simple: query by name
const results = await brain.find({ aggregate: 'monthly_spending' })
// With filtering on group keys
const foodOnly = await brain.find({
aggregate: 'monthly_spending',
where: { category: 'food' }
})
// With sorting and pagination
const topCategories = await brain.find({
aggregate: {
name: 'monthly_spending',
orderBy: 'total',
order: 'desc',
limit: 10
}
})
// Combine find-level params (where, orderBy, limit, offset merge automatically)
const recentFood = await brain.find({
aggregate: 'monthly_spending',
where: { category: 'food' },
orderBy: 'total',
order: 'desc',
limit: 12
})
```
**Result format:** Returns `Result<T>[]` with `type: NounType.Measurement`. Each result contains:
```typescript
{
id: string, // Aggregate group ID (or materialized entity ID)
score: 1.0, // Always 1.0 for aggregates
type: NounType.Measurement,
metadata: {
__aggregate: 'monthly_spending', // Source aggregate name
category: 'food', // Group key values
date: '2024-01', // Time window bucket
total: 342.50, // Computed metrics
count: 28,
average: 12.23,
highest: 45.00,
lowest: 2.50
},
entity: Entity // Full entity structure
}
```
### `queryAggregate(name, params?)` → `Promise<AggregateResult[]>`
The first-class analytics path — returns plain group rows (`{ groupKey, metrics, count }`) instead of `find()`-style `Result` wrappers. Supports `where` (group-key filter), `having` (SQL-HAVING metric filter), `orderBy`, `order`, `limit`, `offset`:
```typescript
const rows = await brain.queryAggregate('monthly_spending', {
having: { total: { greaterThan: 100 } }, // filter by computed metrics
orderBy: 'total',
order: 'desc',
limit: 10
})
// [{ groupKey: { category: 'food', date: '2024-01' }, metrics: { total: 342.5, count: 28 }, count: 28 }, ...]
```
### How It Works
Aggregation hooks run **outside transactions** on every write operation:
- **`add()`**: If the new entity matches any aggregate's `source` filter, its values are added to the matching group's running totals.
- **`update()`**: The old entity's contribution is reversed and the new entity's contribution is applied (handles group key changes, source filter changes).
- **`delete()`**: The deleted entity's contribution is reversed from its group.
**Performance:** O(A × G × M) per write where A = matching aggregates, G = groupBy dimensions, M = metrics. For typical configurations (2-5 aggregates, 1-3 dimensions, 3-5 metrics), this is effectively O(1) — measured at **10,000 entities in 13ms** in unit tests.
**Infinite loop prevention:** Materialized `NounType.Measurement` entities (with `service: 'brainy:aggregation'` or `metadata.__aggregate`) are automatically excluded from all aggregate source matching.
**Persistence:** Definitions and running state are persisted to storage on `flush()`/`close()` and reloaded on `init()`. Definition changes are detected via FNV-1a hashing — only changed aggregates reset their state.
**Native acceleration:** Register an `'aggregation'` provider via the plugin system to replace the TypeScript engine with a custom native implementation for higher throughput at scale.
### Financial Data Modeling
Brainy supports financial analytics through **subtypes and metadata conventions** on existing NounTypes — no custom types needed:
```typescript
// Transaction = NounType.Event + 'transaction' subtype + financial metadata
await brain.add({
data: 'Coffee at Blue Bottle',
type: NounType.Event,
subtype: 'transaction', // top-level standard field (reserved — never in metadata)
metadata: {
domain: 'financial',
amount: 5.50,
currency: 'USD',
category: 'food',
date: Date.now(),
merchant: 'Blue Bottle Coffee'
}
})
// Account = NounType.Collection + 'account' subtype + financial metadata
await brain.add({
data: 'Checking Account',
type: NounType.Collection,
subtype: 'account',
metadata: {
domain: 'financial',
accountType: 'checking',
currency: 'USD',
institution: 'Chase'
}
})
// Invoice = NounType.Document + 'invoice' subtype + financial metadata
await brain.add({
data: 'Invoice #1234 from Acme Corp',
type: NounType.Document,
subtype: 'invoice',
metadata: {
domain: 'financial',
amount: 15000,
currency: 'USD',
status: 'pending',
dueDate: Date.UTC(2024, 2, 15),
vendor: 'Acme Corp'
}
})
```
---
## Relationships
### `relate(params)` → `Promise<string>`
Create a typed relationship between entities.
```typescript
const relId = await brain.relate({
from: sourceId,
to: targetId,
type: VerbType.ReportsTo,
subtype: 'direct', // Optional: sub-classification
data: 'Collaborated on the research paper', // Optional: content for this edge
metadata: { // Optional: structured edge fields
strength: 0.9,
role: 'primary author'
}
})
```
**Parameters:**
- `from`: `string` - Source entity ID (must exist)
- `to`: `string` - Target entity ID (must exist)
- `type`: `VerbType` - Relationship type
- `subtype?`: `string` - Per-product sub-classification within the VerbType (top-level standard field, fast-path indexed). See [Subtypes & Facets](../guides/subtypes-and-facets.md).
- `data?`: `any` - Content for the relationship (overrides auto-computed vector)
- `metadata?`: `object` - Structured edge fields
- `weight?`: `number` - Connection strength (0-1, default: 1.0)
- `bidirectional?`: `boolean` - Create reverse edge too (default: false)
- `confidence?`: `number` - Relationship certainty (0-1)
> **Strict-mode tip:** same as `add()` — if a vocabulary is registered for your `type`, pass a matching `subtype`. Run `await brain.audit()` first to surface pre-existing gaps.
**Returns:** `Promise<string>` - Relationship ID
---
### `updateRelation(params)` → `Promise<void>`
Update an existing relationship. Mirror of `update()` for verbs — closed a long-standing gap (verbs had no update path before 7.30).
```typescript
// Change the subtype on an existing relationship
await brain.updateRelation({ id: relId, subtype: 'dotted-line' })
// Update weight + confidence
await brain.updateRelation({ id: relId, weight: 0.7, confidence: 0.9 })
// Change verb type (re-indexes in graph adjacency, id preserved)
await brain.updateRelation({ id: relId, type: VerbType.WorksWith })
```
**Parameters:**
- `id`: `string` - Relationship ID (required)
- `type?`: `VerbType` - Change verb type (re-indexes in graph adjacency)
- `subtype?`: `string` - Change sub-classification (omit to preserve existing)
- `weight?`: `number` - New weight (0-1)
- `confidence?`: `number` - New confidence (0-1)
- `data?`: `any` - New content
- `metadata?`: `object` - Metadata to merge (or replace with `merge: false`)
- `merge?`: `boolean` - Merge or replace metadata (default: true)
**Returns:** `Promise<void>`
---
### `related(params)` → `Promise<Relation[]>`
Get relationships for an entity. Same name and surface as `db.related()` on a
pinned `Db` view.
```typescript
// Get all relationships FROM an entity
const outgoing = await brain.related({ from: entityId })
// Get all relationships TO an entity
const incoming = await brain.related({ to: entityId })
// Filter by type
const contains = await brain.related({
from: entityId,
type: VerbType.Contains
})
// Filter by subtype (fast path, column-store hit)
const direct = await brain.related({
from: entityId,
type: VerbType.ReportsTo,
subtype: 'direct'
})
// Set membership on subtype
const all = await brain.related({
from: entityId,
type: VerbType.ReportsTo,
subtype: ['direct', 'dotted-line']
})
```
**Parameters:**
- `from?`: `string` - Source entity ID
- `to?`: `string` - Target entity ID
- `type?`: `VerbType | VerbType[]` - Filter by relationship type
- `subtype?`: `string | string[]` - Filter by VerbType subtype (top-level standard field, fast path)
- `service?`: `string` - Multi-tenancy filter
- `limit?`: `number` - Pagination limit (default: 100)
- `offset?`: `number` - Pagination offset
**Returns:** `Promise<Relation[]>` - Matching relationships (each with `subtype` at top level when set)
---
## Batch Operations
### `addMany(params)` → `Promise<BatchResult<string>>`
Add multiple entities in one operation.
```typescript
const result = await brain.addMany({
items: [
{ data: 'Entity 1', type: NounType.Document },
{ data: 'Entity 2', type: NounType.Concept }
]
})
console.log(result.successful) // Array of IDs
console.log(result.failed) // Array of errors
```
**Returns:** `Promise<BatchResult<string>>` - Success/failure results
---
### `removeMany(params)` → `Promise<BatchResult<string>>`
Remove multiple entities.
```typescript
const result = await brain.removeMany({
ids: [id1, id2, id3]
})
```
---
### `updateMany(params)` → `Promise<BatchResult<string>>`
Update multiple entities.
```typescript
const result = await brain.updateMany({
items: [
{ id: id1, metadata: { updated: true } },
{ id: id2, data: 'New content' }
]
})
```
---
### `relateMany(params)` → `Promise<string[]>`
Create multiple relationships.
```typescript
const ids = await brain.relateMany({
items: [
{ from: id1, to: id2, type: VerbType.RelatedTo },
{ from: id1, to: id3, type: VerbType.Contains }
]
})
```
---
## Database Values & Time Travel (Db API)
Brainy 8.0's generational MVCC exposes the whole store as an immutable
value: the **`Db`**. Pin the current state in O(1), commit atomic
multi-write batches, query any past generation with the full query surface,
cut instant snapshots, and ask what-if questions in memory. The exact
guarantees live in the **[consistency model](../concepts/consistency-model.md)**;
recipes live in **[Snapshots & Time Travel](../guides/snapshots-and-time-travel.md)**.
### `generation()` → `number`
The store's current generation — a monotonic watermark advanced once per
committed `transact()` batch and once per single-operation write. Never
reissued, including across restarts and `restore()`.
```typescript
const g = brain.generation()
```
---
### `now()` → `Db`
Pin the current generation and return an immutable view — O(1), no I/O.
The view keeps reading exactly this state no matter what commits afterwards.
```typescript
const db = brain.now()
await brain.update({ id, metadata: { v: 2 } })
await db.get(id) // still sees v: 1 — pinned
await brain.get(id) // sees v: 2 — live
await db.release() // unpin (enables history compaction)
```
**Returns:** `Db` — release it when done; pins gate `compactHistory()`.
---
### `transact(ops, options?)` → `Promise<Db>`
Execute a declarative operation batch **atomically**: either every
operation applies and the store advances exactly one generation, or none
apply and the store is byte-identical to its pre-transaction state. The
commit point is an atomic manifest rename; a crash anywhere before it rolls
back to the exact pre-transaction bytes on the next open.
```typescript
const db = await brain.transact([
{ op: 'add', id: orderId, type: NounType.Document, subtype: 'order', data: 'Order #1042' },
{ op: 'update', id: customerId, metadata: { lastOrderAt: Date.now() }, ifRev: customer._rev },
{ op: 'relate', from: customerId, to: orderId, type: VerbType.Creates, subtype: 'purchase' },
{ op: 'remove', id: staleDraftId },
{ op: 'unrelate', id: oldRelationId }
], {
meta: { author: 'order-service', requestId: 'req-9f2' }, // reified, durable
ifAtGeneration: expectedGeneration // whole-store CAS
})
db.receipt.ids // resolved id per operation, in input order
db.receipt.generation // the committed generation
```
**Operations** (`op` discriminates; parameters mirror the single-operation methods):
- `{ op: 'add', ... }` — same parameters as `add()`; optional explicit `id`
- `{ op: 'update', ... }` — same parameters as `update()`, including per-entity `ifRev` CAS
- `{ op: 'remove', id }` — deletes the entity plus its relationships (same cascade as `delete()`)
- `{ op: 'relate', ... }` — same parameters as `relate()`, including `bidirectional`; duplicates dedupe to the existing relationship id
- `{ op: 'unrelate', id }` — deletes a relationship by id
Operations may reference ids created earlier in the same batch.
**Options:**
- `meta?`: `Record<string, unknown>` — transaction metadata, recorded durably in the transaction log (audit fields: author, reason, request id)
- `ifAtGeneration?`: `number` — whole-store compare-and-swap; commits only if the store is still at this generation
**Returns:** `Promise<Db>` — pinned at the freshly committed generation, carrying a `receipt`.
**Throws:**
- `GenerationConflictError``ifAtGeneration` did not match (nothing staged, generation unchanged)
- `RevisionConflictError` — an `ifRev` did not match (whole batch rejected)
---
### `asOf(target)` → `Promise<Db>`
Open an immutable view of **past** state:
```typescript
const atGen = await brain.asOf(1041) // generation number
const lastWeek = await brain.asOf(new Date(Date.now() - 7 * 86_400_000)) // wall-clock
const fromSnapshot = await brain.asOf('/backups/2026-06-01') // snapshot directory
```
- **`number`** — pins that generation; reads resolve through the immutable record layer.
- **`Date`** — resolved via the transaction log to the newest generation committed at or before it.
- **`string`** — a snapshot directory from `db.persist()`, opened as a self-contained read-only store (equivalent to `Brainy.load()`).
Historical views serve the **full query surface**. Metadata-level reads are
free; the first index-accelerated query (semantic search, traversal,
cursors, aggregation) builds an in-memory index materialization — O(n at
that generation), once per `Db`, freed on `release()`.
**Throws:** `GenerationCompactedError` when the generation's records were reclaimed by `compactHistory()`.
**History granularity:** only `transact()` batches produce historical
records; single-operation writes advance the clock but stay visible through
earlier pins. See the [consistency model](../concepts/consistency-model.md).
---
### `transactionLog(options?)` → `Promise<TxLogEntry[]>`
Read the reified transaction log — one entry per committed `transact()`
batch, newest first: `{ generation, timestamp, meta? }`.
```typescript
const [latest] = await brain.transactionLog({ limit: 1 })
latest.meta // { author: 'order-service', requestId: 'req-9f2' }
```
---
### `compactHistory(options?)` → `Promise<CompactHistoryResult>`
Reclaim historical record-sets that no retention rule and no live `Db` pin
protects. Pinned reads stay correct across compaction, always.
```typescript
await brain.compactHistory({
retainGenerations: 100, // keep the 100 most recent commits
retainMs: 7 * 24 * 60 * 60 * 1000 // and everything from the last 7 days
})
```
**Returns:** `{ removedGenerations, horizon }``asOf()` below the horizon throws `GenerationCompactedError`.
---
### `restore(path, { confirm: true })` → `Promise<void>`
Replace the store's **entire** state from a snapshot directory. Destructive
— requires `{ confirm: true }`. All indexes are rebuilt; the generation
counter is floored so observed generation numbers are never reissued; live
pins do not survive.
```typescript
await brain.restore('/backups/2026-06-01', { confirm: true })
```
---
### `Brainy.load(path)` → `Promise<Db>` (static)
Open a persisted snapshot as a self-contained **read-only** store with the
full query surface, including vector search. Releasing the returned `Db`
closes the underlying instance.
```typescript
const db = await Brainy.load('/backups/2026-06-01')
const hits = await db.search('quarterly invoices')
await db.release()
```
---
### The `Db` value
Every `Db` is pinned at one generation and serves the full query surface at
exactly that state.
**Properties:**
| Property | Type | Meaning |
|---|---|---|
| `generation` | `number` | The pinned generation |
| `timestamp` | `number` | Pin time (`now()`), commit time (`transact()`), or resolved commit time (`asOf()`) |
| `receipt` | `TransactReceipt?` | Present only on `transact()` results |
| `speculative` | `boolean` | Whether this view carries a `with()` overlay |
| `released` | `boolean` | Whether `release()` has been called |
**Methods:**
```typescript
await db.get(id) // entity as of this generation
await db.find({ where: { status: 'open' } }) // full find() surface
await db.search('unpaid invoices') // semantic search as of this generation
await db.related(entityId) // relationships as of this generation
await db.since(olderDb) // ids changed between two views
const whatIf = await db.with(ops) // speculative in-memory overlay
await db.persist('/backups/today') // self-contained hard-link snapshot
await db.release() // unpin + free cached materialization
```
- **`with(ops)`** — applies `transact()`-style operations **in memory** on
top of the view; nothing touches disk, the generation counter, or index
providers. Overlay entities carry no embeddings, so index-accelerated
queries and `persist()` on overlays throw `SpeculativeOverlayError`;
`get()`, metadata-filter `find()`, and filter-based `related()` work
fully. Commit the same ops with `transact()` for the full surface.
- **`persist(path)`** — cuts an instant snapshot (hard links on filesystem
storage; byte copies across devices; in-memory stores serialize to the
same layout). Requires the view to still be the store's latest generation
— otherwise `GenerationConflictError`.
- **`release()`** — idempotent; after release every read throws. A
`FinalizationRegistry` backstop releases leaked pins at GC, but explicit
release is what makes `compactHistory()` deterministic.
### Db API errors
All exported from `@soulcraft/brainy`:
| Error | Thrown by | Meaning |
|---|---|---|
| `GenerationConflictError` | `transact({ ifAtGeneration })`, `db.persist()` | The store moved past the expected generation — re-read and retry |
| `RevisionConflictError` | `update({ ifRev })`, `transact()` update ops | Per-entity revision moved — see [optimistic concurrency](../guides/optimistic-concurrency.md) |
| `GenerationCompactedError` | `asOf()` | The requested generation's records were reclaimed — persist what you must keep |
| `SpeculativeOverlayError` | index-accelerated reads / `persist()` on `with()` overlays | Honest boundary: overlay entities carry no embeddings |
---
## Virtual Filesystem (VFS)
Access via `brain.vfs` (property, not method). Auto-initialized during `brain.init()`.
### Filtering VFS Entities
All VFS entities (files/folders) have `metadata.isVFSEntity: true` set automatically.
Use this to filter VFS entities from semantic search results:
```typescript
// Exclude VFS entities from semantic search
const semanticOnly = await brain.find({
query: 'artificial intelligence',
where: {
isVFSEntity: { notEquals: true } // Only semantic entities
}
})
// Or filter to ONLY VFS entities
const vfsOnly = await brain.find({
where: {
isVFSEntity: { equals: true } // Only VFS files/folders
}
})
// Check if an entity is a VFS entity
if (entity.metadata.isVFSEntity === true) {
console.log('This is a VFS file or folder')
}
```
**Why this matters:** Without filtering, VFS files/folders can appear in concept explorers and semantic search results where they don't belong.
---
### Basic File Operations
#### `vfs.readFile(path, options?)` → `Promise<Buffer>`
Read file content.
```typescript
const content = await brain.vfs.readFile('/docs/README.md')
console.log(content.toString())
```
---
#### `vfs.writeFile(path, data, options?)` → `Promise<void>`
Write file content.
```typescript
await brain.vfs.writeFile('/docs/README.md', 'New content', {
encoding: 'utf-8'
})
```
---
#### `vfs.unlink(path)` → `Promise<void>`
Delete a file.
```typescript
await brain.vfs.unlink('/docs/old-file.md')
```
---
### Directory Operations
#### `vfs.mkdir(path, options?)` → `Promise<void>`
Create directory.
```typescript
await brain.vfs.mkdir('/projects/new-app', { recursive: true })
```
---
#### `vfs.readdir(path, options?)` → `Promise<string[] | Dirent[]>`
List directory contents.
```typescript
const files = await brain.vfs.readdir('/projects')
// With file types
const entries = await brain.vfs.readdir('/projects', { withFileTypes: true })
entries.forEach(entry => {
console.log(entry.name, entry.isDirectory() ? 'DIR' : 'FILE')
})
```
---
#### `vfs.rmdir(path, options?)` → `Promise<void>`
Remove directory.
```typescript
await brain.vfs.rmdir('/old-project', { recursive: true })
```
---
#### `vfs.stat(path)` → `Promise<Stats>`
Get file/directory stats.
```typescript
const stats = await brain.vfs.stat('/docs/README.md')
console.log(stats.size) // File size
console.log(stats.mtime) // Modified time
console.log(stats.isDirectory()) // Is directory?
```
---
### Semantic Operations
#### `vfs.search(query, options?)` → `Promise<SearchResult[]>`
Semantic file search.
```typescript
const results = await brain.vfs.search('React components with hooks', {
path: '/src',
limit: 10
})
```
---
#### `vfs.findSimilar(path, options?)` → `Promise<SearchResult[]>`
Find similar files.
```typescript
const similar = await brain.vfs.findSimilar('/src/App.tsx', {
limit: 5,
threshold: 0.7
})
```
---
### Tree Operations
#### `vfs.getTreeStructure(path, options?)` → `Promise<TreeNode>`
Get directory tree (prevents infinite recursion).
```typescript
const tree = await brain.vfs.getTreeStructure('/projects', {
maxDepth: 3
})
```
---
#### `vfs.getDescendants(path, options?)` → `Promise<VFSEntity[]>`
Get all descendants with optional filtering.
```typescript
const files = await brain.vfs.getDescendants('/src', {
filter: (entity) => entity.name.endsWith('.tsx')
})
```
---
### Metadata & Relationships
#### `vfs.getMetadata(path)` → `Promise<Metadata>`
Get file metadata.
```typescript
const meta = await brain.vfs.getMetadata('/src/App.tsx')
console.log(meta.todos) // Extracted TODOs
console.log(meta.tags) // Tags
```
---
#### `vfs.getRelationships(path)` → `Promise<Relation[]>`
Get file relationships.
```typescript
const rels = await brain.vfs.getRelationships('/src/App.tsx')
// Returns: imports, references, dependencies
```
---
#### `vfs.getTodos(path)` → `Promise<Todo[]>`
Get TODOs from a file.
```typescript
const todos = await brain.vfs.getTodos('/src/App.tsx')
```
---
#### `vfs.searchEntities(query)` → `Promise<Array<{ id, path, type, metadata }>>`
Search for semantic entities tracked by the VFS, filtered by type, name, or metadata.
```typescript
const people = await brain.vfs.searchEntities({
type: 'person', // entity type filter
name: 'Ada', // semantic name search
where: { role: 'author' }, // metadata filters
limit: 50
})
```
---
**[📖 Complete VFS Documentation →](../vfs/QUICK_START.md)**
---
## Neural API
Access advanced AI features via `brain.neural()` (method that returns NeuralAPI instance).
### `neural().similar(a, b, options?)` → `Promise<number | SimilarityResult>`
Calculate semantic similarity.
```typescript
// Simple similarity score
const score = await brain.neural().similar(
'renewable energy',
'sustainable power'
) // 0.87
// Detailed result
const result = await brain.neural().similar('text1', 'text2', {
detailed: true
})
console.log(result.score)
console.log(result.explanation)
```
---
### `neural().clusters(input?, options?)` → `Promise<SemanticCluster[]>`
Automatic clustering. Accepts a text query, an array of texts, or a `ClusteringOptions` object.
```typescript
const clusters = await brain.neural().clusters({
algorithm: 'kmeans', // 'auto' | 'hierarchical' | 'kmeans' | 'dbscan' | ...
maxClusters: 5,
minClusterSize: 3
})
clusters.forEach(cluster => {
console.log(cluster.label) // auto-generated label (when available)
console.log(cluster.members) // entity ids in the cluster
console.log(cluster.centroid) // centroid vector
})
```
---
### `neural().neighbors(id, options?)` → `Promise<NeighborsResult>`
Find nearest neighbors.
```typescript
const result = await brain.neural().neighbors(entityId, {
limit: 10,
minSimilarity: 0.7
})
```
**Options:** `limit?`, `radius?`, `minSimilarity?`, `includeMetadata?`, `sortBy?: 'similarity' | 'importance' | 'recency'`
---
### `neural().outliers(options?)` → `Promise<Outlier[]>`
Detect outlier entities.
```typescript
const outliers = await brain.neural().outliers({ threshold: 0.3 })
// Each outlier carries the entity id plus outlier scoring details
```
**Options:** `threshold?`, `method?: 'isolation' | 'statistical' | 'cluster-based'`, `minNeighbors?`, `includeReasons?`
---
### `neural().visualize(options?)` → `Promise<VizData>`
Generate visualization data.
```typescript
const vizData = await brain.neural().visualize({
maxNodes: 100,
dimensions: 3,
algorithm: 'force',
includeEdges: true
})
// Use with D3.js, Cytoscape, GraphML tools
```
---
### Performance Methods
#### `neural().clusterFast(options?)` → `Promise<SemanticCluster[]>`
Fast clustering for large datasets.
```typescript
const clusters = await brain.neural().clusterFast({
maxClusters: 10
})
```
**Options:** `level?` (hierarchy level), `maxClusters?`
---
#### `neural().clusterLarge(options?)` → `Promise<SemanticCluster[]>`
Sampling-based clustering for very large datasets.
```typescript
const clusters = await brain.neural().clusterLarge({
sampleSize: 1000,
strategy: 'diverse' // 'random' | 'diverse' | 'recent'
})
```
---
## Import & Export
### `import(source, options?)` → `Promise<ImportResult>`
Smart import with auto-detection (CSV, Excel, PDF, JSON, URLs).
```typescript
// CSV import
await brain.import('data.csv', {
format: 'csv',
createEntities: true
})
// Excel import (all sheets processed automatically)
await brain.import('sales.xlsx', {
format: 'excel',
vfsPath: '/imports/sales', // optional: mirror into the VFS
groupBy: 'sheet'
})
// PDF import (tables extracted automatically)
await brain.import('research.pdf', { format: 'pdf' })
// URL import
await brain.import('https://api.example.com/data.json')
```
**Parameters:**
- `source`: `string | Buffer | object` - File path, URL, buffer, or object
- `options?`: Import configuration
- `format?`: `'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx' | 'image'` - Auto-detected if omitted
- `vfsPath?`: `string` - Mirror imported content into the VFS at this path
- `groupBy?`: `'type' | 'sheet' | 'flat' | 'custom'` - VFS grouping strategy
- `createEntities?`: `boolean` - Create entities from rows
- `createRelationships?`: `boolean` - Create relationships between extracted entities
- `preserveSource?`: `boolean` - Save the original file in the VFS
- `enableNeuralExtraction?`: `boolean` - Extract entity names via AI
- `enableRelationshipInference?`: `boolean` - Infer relationships via AI
- `enableConceptExtraction?`: `boolean` - Extract entity types via AI
- `confidenceThreshold?`: `number` - Minimum confidence for extracted entities
- `onProgress?`: `(progress) => void` - Progress callback (stage, counts, throughput, ETA)
**Returns:** `Promise<ImportResult>` - Import statistics
**[📖 Complete Import Guide →](../guides/import-anything.md)**
---
### Export & Import (portable) + Snapshots (native)
**Portable graph backup**`brain.export()` / `brain.import()` (`BackupData` v1, versioned
JSON, partial-or-whole, cross-version). `export()` lives on the immutable `Db`, so it composes
with `now()`/`asOf()`/`with()`:
```typescript
// Export part or all of the brain to a portable, versioned document
const backup = await brain.export({ ids }, { includeVectors: true })
// Restore it — import() routes a BackupData to the graph round-trip (merge by id)
await otherBrain.import(backup, { onConflict: 'merge' })
// Time-travel export (serialize a past generation) / what-if export (a speculative state)
const past = await brain.asOf(gen)
const asWas = await past.export({ collection: id })
await past.release()
await brain.now().with(ops).export({ ids })
```
Selectors: `{ ids }`, `{ collection }` (alias `memberOf`), `{ connected: { from, depth } }`,
`{ vfsPath }`, predicate (`{ type, subtype, where, service }`), or whole brain (omit). See the
**[Export & Import guide](../guides/export-and-import.md)**. Distinct from `brain.import(file)`
(CSV/PDF/Excel/JSON ingestion — `import()` dispatches on whether you pass a `BackupData` or a file).
**Native whole-brain snapshot** (generation-preserving, not portable JSON):
```typescript
// Instant hard-link snapshot via the Db API
const pin = brain.now()
await pin.persist('/backups/2026-06-11')
await pin.release()
// Time-travel to a past generation or timestamp
const snapshot = await brain.asOf(new Date('2026-06-01'))
const entities = await snapshot.find({ limit: 100 })
await snapshot.release()
```
---
## Configuration
### Constructor Options
```typescript
const brain = new Brainy({
// Storage configuration
storage: {
type: 'filesystem', // 'memory' | 'filesystem' | 'auto'
rootDirectory: './brainy-data'
},
// Vector index configuration (2 knobs)
vector: {
recall: 'balanced', // 'fast' | 'balanced' | 'accurate'
persistMode: 'immediate' // 'immediate' | 'deferred'
},
// Model configuration (embedded in WASM - zero config needed)
// Model: all-MiniLM-L6-v2 (384 dimensions)
// Device: CPU via WASM (works everywhere)
// Cache configuration — `true`/`false`, or an options object
cache: {
maxSize: 10000,
ttl: 3600000 // 1 hour in ms
}
})
await brain.init() // Required! VFS auto-initialized
```
---
## Storage Adapters
Brainy 8.0 ships two adapters — both support the full Db API (generational history, snapshots, restore).
### Memory (Default for Tests)
```typescript
const brain = new Brainy({
storage: { type: 'memory' }
})
```
**Use case:** Development, testing, prototyping
---
### Filesystem (Default for Node)
```typescript
const brain = new Brainy({
storage: {
type: 'filesystem',
rootDirectory: './brainy-data'
}
})
```
**Use case:** Node.js applications, single-node production deployments
For off-site backup, snapshot `rootDirectory` from your scheduler (`gsutil rsync`, `aws s3 sync`, `rclone`, or `tar`) — Brainy itself doesn't reach out to cloud object stores.
---
### Auto
```typescript
const brain = new Brainy({
storage: { type: 'auto', rootDirectory: './brainy-data' }
})
```
Picks `'filesystem'` on Node with a writable `rootDirectory`, falls back to `'memory'` otherwise.
---
## Utility Methods
### `clear()` → `Promise<void>`
Clear all data (entities and relationships).
```typescript
await brain.clear()
```
---
### `getNounCount()` → `Promise<number>`
Get total entity count.
```typescript
const count = await brain.getNounCount()
```
---
### `getVerbCount()` → `Promise<number>`
Get total relationship count.
```typescript
const count = await brain.getVerbCount()
```
---
### Subtype & facet APIs
Full guide: **[Subtypes & Facets](../guides/subtypes-and-facets.md)**.
#### `counts.bySubtype(type, subtype?)` → `Record<string, number> | number`
O(1) subtype counts for a NounType (backed by the persisted rollup).
```typescript
brain.counts.bySubtype(NounType.Person)
// → { employee: 12, customer: 847, vendor: 34 }
brain.counts.bySubtype(NounType.Person, 'employee')
// → 12
```
#### `counts.topSubtypes(type, n=10)` → `Array<[subtype, count]>`
Top N subtypes ranked by count.
```typescript
brain.counts.topSubtypes(NounType.Person, 3)
// → [['customer', 847], ['employee', 12], ['vendor', 34]]
```
#### `subtypesOf(type)` → `string[]`
Sorted distinct subtypes seen for a NounType.
```typescript
brain.subtypesOf(NounType.Person)
// → ['customer', 'employee', 'vendor']
```
#### `counts.byRelationshipSubtype(verb, subtype?)` → `Record<string, number> | number`
Verb-side mirror of `counts.bySubtype`. O(1) per-VerbType-per-subtype counts.
```typescript
brain.counts.byRelationshipSubtype(VerbType.ReportsTo)
// → { direct: 12, 'dotted-line': 3 }
brain.counts.byRelationshipSubtype(VerbType.ReportsTo, 'direct')
// → 12
```
#### `counts.topRelationshipSubtypes(verb, n=10)` → `Array<[subtype, count]>`
Top N subtypes for a `VerbType` ranked by count.
```typescript
brain.counts.topRelationshipSubtypes(VerbType.ReportsTo, 3)
// → [['direct', 12], ['dotted-line', 3]]
```
#### `relationshipSubtypesOf(verb)` → `string[]`
Sorted distinct subtypes seen for a `VerbType`.
```typescript
brain.relationshipSubtypesOf(VerbType.ReportsTo)
// → ['direct', 'dotted-line']
```
#### `audit(options?)` → `Promise<AuditReport>` (7.30.1+)
Diagnostic — find entities and relationships missing a `subtype` value, grouped by type. The companion to `migrateField()` / `fillSubtypes()` — answers "what would break if I enabled strict subtype enforcement?".
```typescript
const report = await brain.audit()
// {
// entitiesWithoutSubtype: { event: 24, document: 3 },
// relationshipsWithoutSubtype: { relatedTo: 1402 },
// total: 1429,
// scanned: 8400,
// recommendation: 'Found 1429 entries without subtype. ...'
// }
```
**Parameters:**
- `options.includeVFS?`: `boolean` — When `false` (default), VFS infrastructure entities (`metadata.isVFSEntity` / `metadata.isVFS`) are excluded. They bypass enforcement anyway, so counting them is noise.
- `options.batchSize?`: `number` — Pagination batch size (default 200).
- `options.onProgress?`: `(progress: { scanned, missingSubtype }) => void` — Progress callback per batch.
Run before adopting an SDK that registers `requireSubtype()` rules, or before upgrading to Brainy 8.0 (which makes strict mode the default). See the [Strict mode in practice](../guides/subtypes-and-facets.md#strict-mode-in-practice-for-sdk-style-vocabulary-consumers) guide for the full migration recipe.
#### `requireSubtype(type, options?)` → `void`
Register subtype enforcement for a specific `NounType` or `VerbType`. Unified API for nouns and verbs. Composes with the brain-wide `requireSubtype` constructor flag.
```typescript
// Lock down Person sub-classification
brain.requireSubtype(NounType.Person, {
values: ['employee', 'customer', 'vendor'],
required: true
})
// Lock down management edges
brain.requireSubtype(VerbType.ReportsTo, {
values: ['direct', 'dotted-line'],
required: true
})
```
**Parameters:**
- `type`: `NounType | VerbType` - The type to register
- `options.values?`: `string[]` - Vocabulary whitelist (rejects off-vocab values)
- `options.required?`: `boolean` - Whether subtype is required (default: `true`)
#### Brain-wide strict mode — `new Brainy({ requireSubtype })`
Constructor option that enforces subtype on every `add()` / `addMany()` / `update()` / `relate()` / `relateMany()` / `updateRelation()` for every type:
```typescript
// Every write must include subtype
const brain = new Brainy({ requireSubtype: true })
// Exempt specific types (e.g. catch-all Thing)
const brain2 = new Brainy({
requireSubtype: { except: [NounType.Thing, NounType.Custom] }
})
```
When strict mode is on:
- Every public write path checks the pairing guarantee.
- `addMany()` / `relateMany()` validate all items BEFORE any storage write — atomic-fail, no partial writes.
- Brainy's own VFS infrastructure writes bypass via the `metadata.isVFSEntity: true` marker.
- Per-type registrations always apply regardless of the brain-wide flag.
The default since 8.0.0 — pass `requireSubtype: false` to opt out while migrating pre-8.0 data.
#### `trackField(name, options?)` → `void`
Register a metadata field for cardinality + per-NounType breakdown stats. With `values: [...]`, validates against the whitelist on `add()`/`update()`.
```typescript
brain.trackField('status') // basic
brain.trackField('status', { perType: true }) // with per-NounType breakdown
brain.trackField('priority', { values: ['low', 'med', 'high'] }) // strict vocabulary
```
#### `counts.byField(name, options?)` → `Promise<Record<string, number>>`
Counts by value for a tracked field. Requires `perType: true` registration if filtering by NounType.
```typescript
await brain.counts.byField('status')
// → { todo: 12, doing: 3, done: 47 }
await brain.counts.byField('status', { type: NounType.Task })
// → { todo: 8, doing: 2, done: 30 }
```
#### `migrateField(options)` → `Promise<MigrationSummary>`
Stream-and-rewrite a field across the brain. Supports `metadata.X`, `data.X`, and top-level paths. Idempotent.
```typescript
// One-shot rewrite
await brain.migrateField({ from: 'metadata.kind', to: 'subtype' })
// Deprecation window — keep source field readable
await brain.migrateField({ from: 'data.kind', to: 'subtype', readBoth: true })
// With progress reporting
await brain.migrateField({
from: 'metadata.kind',
to: 'subtype',
batchSize: 500,
onProgress: ({ scanned, migrated }) => console.log(`${scanned} / ${migrated}`)
})
```
Returns `{ scanned: number, migrated: number, skipped: number, errors: Array<{id, error}> }`.
#### `fillSubtypes(rules, options?)` → `Promise<FillSubtypesResult>` (8.0+)
Back-fill missing `subtype` values across entities AND relationships in one streaming pass — the migration companion to `audit()`. Keys are `NounType`/`VerbType` values; each rule is a literal subtype string or a function deriving one from the entry (return `undefined` to decline). Idempotent: entries that already carry a subtype are never touched, so a crashed run is resumed safely by re-running.
```typescript
const report = await brain.fillSubtypes({
[NounType.Person]: (e) => e.metadata?.kind ?? 'unspecified', // derived
[NounType.Document]: 'general', // literal default
[VerbType.RelatedTo]: 'unspecified' // relationship rule
})
// → { scanned, filled, skipped, errors, byType }
```
**Parameters:**
- `rules`: `FillSubtypeRules` - Map of NounType/VerbType → literal subtype or `(entry) => string | undefined`
- `options.includeVFS?`: `boolean` - Also fill VFS infrastructure entries (default `false`)
- `options.batchSize?`: `number` - Pagination batch size (default `200`)
- `options.onProgress?`: `(progress: { scanned, filled, skipped }) => void` - Per-batch callback
Returns `{ scanned, filled, skipped, errors, byType }`. After a clean run, `skipped` equals the remaining `audit().total`. See the [migration recipe](../guides/subtypes-and-facets.md).
---
### `embed(data)` → `Promise<number[]>` ✨
Generate embedding vector from text or data.
```typescript
const vector = await brain.embed('Hello world')
// 384-dimensional vector
console.log(vector.length) // 384
```
---
### `embedBatch(texts)` → `Promise<number[][]>` ✨
Batch embed multiple texts using native WASM batch API (single forward pass).
```typescript
const embeddings = await brain.embedBatch([
'Machine learning is fascinating',
'Deep neural networks',
'Natural language processing'
])
console.log(embeddings.length) // 3
console.log(embeddings[0].length) // 384
```
> Uses the WASM engine's native `embed_batch()` for a single model forward pass instead of N individual calls. This is the same batch API used internally by `highlight()`.
---
### `similarity(textA, textB)` → `Promise<number>` ✨
Calculate semantic similarity between two texts.
```typescript
const score = await brain.similarity(
'The cat sat on the mat',
'A feline was resting on the rug'
)
console.log(score) // ~0.85 (high semantic similarity)
```
**Returns:** Score from 0 (different) to 1 (identical meaning)
---
### `neighbors(entityId, options?)` → `Promise<string[]>` ✨
Get graph neighbors of an entity.
```typescript
// Get all connected entities
const neighbors = await brain.neighbors(entityId)
// Get outgoing connections only
const outgoing = await brain.neighbors(entityId, {
direction: 'outgoing',
limit: 10
})
// Multi-hop traversal
const extended = await brain.neighbors(entityId, {
depth: 2,
direction: 'both'
})
```
**Options:**
- `direction`: `'outgoing' | 'incoming' | 'both'` (default: 'both')
- `depth`: `number` - Traversal depth (default: 1)
- `verbType`: `VerbType` - Filter by relationship type
- `limit`: `number` - Maximum neighbors to return
---
### `findDuplicates(options?)` → `Promise<DuplicateResult[]>` ✨
Find semantic duplicates in the database.
```typescript
// Find all duplicates
const duplicates = await brain.findDuplicates()
for (const group of duplicates) {
console.log('Original:', group.entity.id)
for (const dup of group.duplicates) {
console.log(` Duplicate: ${dup.entity.id} (${dup.similarity.toFixed(2)})`)
}
}
// Find person duplicates with higher threshold
const personDupes = await brain.findDuplicates({
type: NounType.Person,
threshold: 0.9,
limit: 50
})
```
**Options:**
- `threshold`: `number` - Minimum similarity (default: 0.85)
- `type`: `NounType` - Filter by entity type
- `limit`: `number` - Maximum duplicate groups (default: 100)
---
### `indexStats()` → `Promise<IndexStats>` ✨
Get comprehensive index statistics.
```typescript
const stats = await brain.indexStats()
console.log(`Entities: ${stats.entities}`)
console.log(`Vectors: ${stats.vectors}`)
console.log(`Relationships: ${stats.relationships}`)
console.log(`Memory: ${(stats.memoryUsage.total / 1024 / 1024).toFixed(1)}MB`)
console.log(`Fields: ${stats.metadataFields.join(', ')}`)
```
**Returns:**
- `entities` - Total entity count
- `vectors` - Total vectors in the vector index
- `relationships` - Total relationships in graph
- `metadataFields` - Indexed metadata fields
- `memoryUsage.vectors` - Vector memory (bytes)
- `memoryUsage.graph` - Graph memory (bytes)
- `memoryUsage.metadata` - Metadata index memory (bytes)
- `memoryUsage.total` - Total memory usage
---
### `cluster(options?)` → `Promise<ClusterResult[]>` ✨
Cluster entities by semantic similarity.
```typescript
// Find all clusters
const clusters = await brain.cluster()
for (const cluster of clusters) {
console.log(`${cluster.clusterId}: ${cluster.entities.length} entities`)
}
// Find document clusters with centroids
const docClusters = await brain.cluster({
type: NounType.Document,
threshold: 0.85,
minClusterSize: 3,
includeCentroid: true
})
```
**Options:**
- `threshold`: `number` - Similarity threshold (default: 0.8)
- `type`: `NounType` - Filter by entity type
- `minClusterSize`: `number` - Minimum cluster size (default: 2)
- `limit`: `number` - Maximum clusters to return (default: 100)
- `includeCentroid`: `boolean` - Calculate cluster centroids (default: false)
**Returns:**
- `clusterId` - Unique cluster identifier
- `entities` - Array of entities in the cluster
- `centroid` - Average embedding vector (if includeCentroid is true)
---
### `getStats(options?)` → `Promise<Statistics>`
Get complete entity/relationship statistics (convenience wrapper over `brain.counts`).
```typescript
const stats = await brain.getStats()
console.log(stats.entities.total) // total entity count
console.log(stats.entities.byType) // counts per NounType
console.log(stats.relationships) // relationship stats
console.log(stats.density) // relationships per entity
// Exclude VFS infrastructure entities from the counts
const semanticOnly = await brain.getStats({ excludeVFS: true })
```
---
## Lifecycle
### Initialization
```typescript
const brain = new Brainy(config)
await brain.init() // Required! VFS auto-initialized here
```
VFS is auto-initialized during `brain.init()` - no separate `vfs.init()` needed!
---
### Shutdown
```typescript
await brain.close() // Graceful shutdown — flushes pending writes and releases the writer lock
```
---
## Examples
### Basic CRUD
```typescript
// Create
const id = await brain.add({
data: 'Quantum computing breakthrough',
type: NounType.Concept,
metadata: { category: 'tech', year: 2024 }
})
// Read
const entity = await brain.get(id)
// Update
await brain.update({
id,
metadata: { updated: true }
})
// Remove
await brain.remove(id)
```
---
### Knowledge Graphs
```typescript
// Create entities
const ai = await brain.add({
data: 'Artificial Intelligence',
type: NounType.Concept
})
const ml = await brain.add({
data: 'Machine Learning',
type: NounType.Concept
})
// Create relationship
await brain.relate({
from: ml,
to: ai,
type: VerbType.IsA
})
// Traverse graph
const results = await brain.find({
connected: { from: ai, depth: 2 }
})
```
---
### Triple Intelligence Query
```typescript
const results = await brain.find({
query: 'modern frontend frameworks', // 🔍 Vector
where: { // 📊 Document
year: { greaterThan: 2020 },
category: { oneOf: ['framework', 'library'] }
},
connected: { // 🕸️ Graph
to: reactId,
depth: 2,
type: VerbType.BuiltOn
},
limit: 10
})
```
---
### Database-as-a-Value Workflow
```typescript
// Speculate: what would this change look like? (nothing touches disk)
const base = brain.now()
const whatIf = await base.with([
{ op: 'add', type: NounType.Document, subtype: 'note', data: 'New feature', metadata: { draft: true } }
])
await whatIf.find({ where: { draft: true } })
await whatIf.release()
await base.release()
// Commit it for real — one atomic generation, with audit metadata
await brain.transact(
[{ op: 'add', type: NounType.Document, subtype: 'note', data: 'New feature', metadata: { draft: true } }],
{ meta: { author: 'dev@example.com', message: 'Add new feature' } }
)
```
---
### VFS File Management
```typescript
// Write files
await brain.vfs.writeFile('/docs/README.md', 'Project documentation')
await brain.vfs.mkdir('/src/components', { recursive: true })
// Read files
const content = await brain.vfs.readFile('/docs/README.md')
// Semantic search
const reactFiles = await brain.vfs.search('React components with hooks', {
path: '/src'
})
// Get tree structure (safe, prevents infinite recursion)
const tree = await brain.vfs.getTreeStructure('/projects', {
maxDepth: 3
})
```
---
## Type System Reference
Stage 3 CANONICAL taxonomy with 169 types (42 nouns + 127 verbs)
### Noun Types (42)
Brainy uses a comprehensive noun type system covering 96-97% of human knowledge:
**Core Entity Types (7)**
- `NounType.Person` - Individual human entities
- `NounType.Organization` - Companies, institutions, collectives
- `NounType.Location` - Geographic and spatial entities
- `NounType.Thing` - Physical objects and artifacts
- `NounType.Concept` - Abstract ideas and principles
- `NounType.Event` - Temporal occurrences
- `NounType.Agent` - AI agents, bots, automated systems
**Digital/Content Types (4)**
- `NounType.Document` - Text-based files and written content
- `NounType.Media` - Audio, video, images
- `NounType.File` - Generic digital files
- `NounType.Message` - Communication content
**Business Types (4)**
- `NounType.Product` - Commercial products
- `NounType.Service` - Service offerings
- `NounType.Task` - Actions, todos, work items
- `NounType.Project` - Organized initiatives
**Scientific Types (2)**
- `NounType.Hypothesis` - Theories and propositions
- `NounType.Experiment` - Studies and investigations
**And 25 more types** including: `Organism`, `Substance`, `Quality`, `TimeInterval`, `Function`, `Proposition`, `Collection`, `Dataset`, `Process`, `State`, `Role`, `Language`, `Currency`, `Measurement`, `Contract`, `Regulation`, `Interface`, `Resource`, `Custom`, `SocialGroup`, `Institution`, `Norm`, `InformationContent`, `InformationBearer`, `Relationship`
### Verb Types (127)
Brainy supports 127 relationship types organized into categories:
**Foundational (7)**
- `VerbType.InstanceOf`, `VerbType.SubclassOf`, `VerbType.ParticipatesIn`
- `VerbType.RelatedTo`, `VerbType.Contains`, `VerbType.PartOf`, `VerbType.References`
**Spatial & Temporal (14)**
- Location: `LocatedAt`, `AdjacentTo`, `ContainsSpatially`, `OverlapsSpatially`, `Above`, `Below`, `Inside`, `Outside`, `Facing`
- Time: `Precedes`, `During`, `OccursAt`, `Overlaps`, `ImmediatelyAfter`, `SimultaneousWith`
**Causal & Dependency (11)**
- Direct: `Causes`, `Enables`, `Prevents`, `DependsOn`, `Requires`
- Modal: `CanCause`, `MustCause`, `WouldCauseIf`, `ProbablyCauses`
- Variations: `RigidlyDependsOn`, `FunctionallyDependsOn`, `HistoricallyDependsOn`
**Creation & Change (10)**
- Lifecycle: `Creates`, `Transforms`, `Becomes`, `Modifies`, `Consumes`, `Destroys`
- Properties: `GainsProperty`, `LosesProperty`, `RemainsSame`, `PersistsThrough`
**Social & Communication (8)**
- `MemberOf`, `WorksWith`, `FriendOf`, `Follows`, `Likes`, `ReportsTo`, `Mentors`, `Communicates`
**Epistemic & Modal (14)**
- Knowledge: `Knows`, `Doubts`, `Believes`, `Learns`
- Mental states: `Desires`, `Intends`, `Fears`, `Loves`, `Hates`, `Hopes`, `Perceives`
- Modality: `CouldBe`, `MustBe`, `Counterfactual`
**Measurement & Comparison (9)**
- `Measures`, `MeasuredIn`, `ConvertsTo`, `HasMagnitude`, `GreaterThan`
- `SimilarityDegree`, `ApproximatelyEquals`, `MoreXThan`, `HasDegree`
**And 54 more specialized verbs** including ownership, composition, uncertainty, deontic relationships (obligations/permissions), context-dependent truth, spatial/temporal variations, information theory, and meta-level relationships.
### Complete Reference
For the full taxonomy with all 169 types and their descriptions, see:
- **[Stage 3 CANONICAL Taxonomy](../STAGE3-CANONICAL-TAXONOMY.md)** - Complete list with categories
- **[Noun-Verb Taxonomy Architecture](../architecture/noun-verb-taxonomy.md)** - Design rationale
### Migration from pre-Stage-3 taxonomies
**Breaking Changes:**
- `NounType.Content` removed → Use `Document`, `Message`, or `InformationContent`
- `NounType.User` removed → Use `Person` or `Agent`
- `NounType.Topic` removed → Use `Concept` or `Category`
**New Types Added:**
- **+11 noun types**: Agent, Organism, Substance, Quality, TimeInterval, Function, Proposition, Custom, SocialGroup, Institution, Norm, InformationContent, InformationBearer, Relationship
- **+87 verb types**: Extensive additions across all categories
---
## Key Features
-**Database as a Value** - `brain.now()` pins the whole store as an immutable `Db` in O(1)
-**Atomic Transactions** - `brain.transact()` commits multi-write batches all-or-nothing
-**Two-Level CAS** - per-entity `ifRev` and whole-store `ifAtGeneration`
-**Time Travel** - `brain.asOf()` serves the full query surface at any reachable past generation
-**Instant Snapshots** - `db.persist()` cuts hard-link snapshots; `Brainy.load()` opens them read-only
-**Speculative Writes** - `db.with()` answers what-if questions purely in memory
-**Reified Transaction Metadata** - audit fields recorded durably, readable via `transactionLog()`
-**VFS Entity Filtering** - All VFS entities have the `isVFSEntity: true` flag
-**VFS Auto-Initialization** - No separate `vfs.init()` calls
-**VFS Property Access** - Use `brain.vfs.method()` instead of `brain.vfs().method()`
-**Universal Storage Support** - Filesystem and memory adapters share one on-disk contract
---
## Support & Resources
- **📖 Documentation:** [Full Documentation](../)
- **🐛 Issues:** [GitHub Issues](https://github.com/soulcraftlabs/brainy/issues)
- **💬 Discussions:** [GitHub Discussions](https://github.com/soulcraftlabs/brainy/discussions)
- **📦 NPM:** [@soulcraft/brainy](https://www.npmjs.com/package/@soulcraft/brainy)
- **⭐ GitHub:** [Star us](https://github.com/soulcraftlabs/brainy)
---
## See Also
- **[Data Model](../DATA_MODEL.md)** - Entity structure, data vs metadata, storage fields
- **[Query Operators](../QUERY_OPERATORS.md)** - All BFO operators with examples and indexed vs in-memory matrix
- **[Triple Intelligence Architecture](../architecture/triple-intelligence.md)** - How vector + graph + document work together
- **[Find System](../FIND_SYSTEM.md)** - Natural language find() details
- **[VFS Quick Start](../vfs/QUICK_START.md)** - Complete VFS documentation
- **[Import Anything Guide](../guides/import-anything.md)** - CSV, Excel, PDF, URL imports
- **[Consistency Model](../concepts/consistency-model.md)** - The guarantees behind the Db API
- **[Snapshots & Time Travel](../guides/snapshots-and-time-travel.md)** - Backup, restore, what-if, audit recipes
---
**License:** MIT © Brainy Contributors
---
*Brainy - The Knowledge Operating System*
*From prototype to planet-scale • Zero configuration • Triple Intelligence™ • Database as a Value*