---
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}}` |
| `gte` / `greaterThanOrEqual` | Greater or equal | `{score: {gte: 90}}` |
| `lessThan` / `lt` | Less than | `{price: {lessThan: 100}}` |
| `lte` / `lessThanOrEqual` | Less or equal | `{rating: {lte: 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:** every write is its own immutable generation —
`transact()` batches AND single-operation writes — so a pin always freezes and
every write is addressable via `asOf()`. See the
[consistency model](../concepts/consistency-model.md).

---

### `transactionLog(options?)` → `Promise<TxLogEntry[]>`

Read the reified transaction log — one entry per committed generation (every
`transact()` AND single-op write), newest first: `{ generation, timestamp,
meta? }`. Single-op generations carry no `meta` (it is a `transact()`-only field).

```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 cap and no live `Db` pin
protects. Pinned reads stay correct across compaction, always. (Auto-compaction
on `flush()`/`close()` is governed by the constructor `retention` knob — unset →
adaptive, `'all'` → unbounded, `{ … }` → explicit caps.)

```typescript
await brain.compactHistory({
  maxGenerations: 100,               // keep at most the 100 most recent generations
  maxAge: 7 * 24 * 60 * 60 * 1000    // and only those 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.find({ query: '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.find({ query: '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)**

---

## 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 export/import** — `brain.export()` / `brain.import()` (`PortableGraph` 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 graph = await brain.export({ ids }, { includeVectors: true })

// Restore it — import() routes a PortableGraph to the graph round-trip (merge by id)
await otherBrain.import(graph, { 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 `PortableGraph` 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'
    path: './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',
    path: './brainy-data'
  }
})
```

**Use case:** Node.js applications, single-node production deployments

For off-site backup, snapshot `path` 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', path: './brainy-data' }
})
```

Picks `'filesystem'` on Node with a writable `path`, 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*
