Branching & Time Travel

Git-style version control for your entire knowledge graph. Fork, commit, branch, and time-travel through your data.

Unique to Brainy

No other vector database offers Git-style branching. Fork your entire knowledge graph in <100ms using copy-on-write technology.

Overview

While the Versioning API tracks individual entities, the Branching API provides workspace-level version control. Create isolated branches for experiments, track commits with full state snapshots, and query historical data.

How It Works: Copy-on-Write (COW)

Brainy uses Copy-on-Write technology for instant forks. When you fork:

Result: Fork 1M entities in <100ms, with 10-20% memory overhead.

Methods

fork(branch?, options?)

Create an instant clone of your knowledge graph. Changes in the fork don't affect the parent.

fork-examples.js
import { Brainy, NounType } from '@soulcraft/brainy'

const brain = new Brainy()
await brain.init()

// Add some data
await brain.add({ data: 'Alice', type: NounType.Person })
await brain.add({ data: 'Bob', type: NounType.Person })

// Fork instantly (<100ms)
const experiment = await brain.fork('experiment')

// Make changes in the fork
await experiment.add({ data: 'Charlie', type: NounType.Person })

// Original is untouched
console.log((await brain.find({})).length)       // 2 (Alice, Bob)
console.log((await experiment.find({})).length)  // 3 (Alice, Bob, Charlie)

// Fork with metadata
const backup = await brain.fork('backup-2024-01', {
  author: 'admin@example.com',
  message: 'Monthly backup',
  metadata: { backupType: 'monthly' }
})

listBranches()

List all branches in the workspace.

const branches = await brain.listBranches()
console.log(branches)  // ['main', 'experiment', 'backup-2024-01']

getCurrentBranch()

Get the current branch name.

const current = await brain.getCurrentBranch()
console.log(current)  // 'main'

checkout(branch)

Switch to a different branch.

// Switch to experiment branch
await brain.checkout('experiment')
console.log(await brain.getCurrentBranch())  // 'experiment'

// Switch back to main
await brain.checkout('main')

commit(options?)

Create a commit with the current state. Commits capture a full snapshot for time-travel.

commit-examples.js
// Simple commit
const hash = await brain.commit()

// Commit with message
const hash2 = await brain.commit({
  message: 'Add user data',
  author: 'dev@example.com'
})

// Commit with full state capture (for time-travel)
const snapshot = await brain.commit({
  message: 'Before migration',
  captureState: true  // Serialize ALL entities + relationships
})

// Commit with custom metadata
const tagged = await brain.commit({
  message: 'Release v2.0',
  metadata: {
    version: '2.0.0',
    releaseNotes: 'Major update'
  }
})

asOf(commitId)

Create a read-only snapshot at a specific commit. This is time-travel - query historical data!

time-travel.js
// Make some changes and commit
await brain.add({ data: 'Version 1 data', type: NounType.Document })
const commit1 = await brain.commit({ captureState: true })

// Make more changes
await brain.add({ data: 'Version 2 data', type: NounType.Document })
await brain.commit({ captureState: true })

// Time travel to commit1
const snapshot = await brain.asOf(commit1)

// Query historical state (full Triple Intelligence works!)
const results = await snapshot.find({ query: 'data' })
console.log(results.length)  // 1 (only Version 1)

// Get historical relationships
const relations = await snapshot.getRelations()

// IMPORTANT: Close when done to free memory
await snapshot.close()
Important

Snapshots from asOf() are read-only. Attempting to add, update, delete, or commit will throw an error. Always call snapshot.close() when done to free memory.

getHistory(options?)

Get commit history for the current branch.

history-examples.js
// Get last 10 commits
const history = await brain.getHistory({ limit: 10 })

history.forEach(commit => {
  console.log(`${commit.hash}: ${commit.message}`)
  console.log(`  by ${commit.author} at ${new Date(commit.timestamp)}`)
})

// Paginate through history
const page2 = await brain.getHistory({ limit: 10, offset: 10 })

// Filter by author
const myCommits = await brain.getHistory({
  author: 'dev@example.com'
})

streamHistory(options?)

Stream commit history for large histories (memory-efficient).

// Stream through thousands of commits
for await (const commit of brain.streamHistory({ limit: 10000 })) {
  console.log(`${commit.timestamp}: ${commit.message}`)
}

// Stream with time filter
for await (const commit of brain.streamHistory({
  since: Date.now() - 86400000  // Last 24 hours
})) {
  // Process commit
}

deleteBranch(branch)

Delete a branch.

await brain.deleteBranch('old-experiment')

Use Cases

Safe Migrations

safe-migration.js
async function safeMigration(brain, migrationFn) {
  // Create a commit before migration
  const beforeCommit = await brain.commit({
    message: 'Pre-migration snapshot',
    captureState: true
  })

  try {
    // Run migration
    await migrationFn(brain)

    // Commit after migration
    await brain.commit({
      message: 'Post-migration',
      captureState: true
    })

    console.log('Migration successful!')
  } catch (error) {
    // Restore to pre-migration state
    const snapshot = await brain.asOf(beforeCommit)
    console.log('Migration failed, state preserved at:', beforeCommit)
    throw error
  }
}

A/B Testing Features

ab-testing.js
// Create branches for A/B testing
const branchA = await brain.fork('feature-a')
const branchB = await brain.fork('feature-b')

// Apply different configurations
await branchA.add({ data: 'Config A', type: NounType.Document })
await branchB.add({ data: 'Config B', type: NounType.Document })

// Route users to different branches
function getBrainForUser(userId) {
  return userId % 2 === 0 ? branchA : branchB
}

Audit Trail

audit-trail.js
async function auditedUpdate(brain, entityId, changes, userId) {
  // Get state before change
  const before = await brain.get(entityId)

  // Make the change
  await brain.update({ id: entityId, ...changes })

  // Commit with audit info
  await brain.commit({
    message: `Updated ${entityId}`,
    author: userId,
    captureState: true,
    metadata: {
      entityId,
      action: 'update',
      changes: Object.keys(changes),
      timestamp: Date.now()
    }
  })
}

// Query audit history
async function getEntityAuditTrail(brain, entityId) {
  const history = await brain.getHistory()
  return history.filter(c => c.metadata?.entityId === entityId)
}

Performance Characteristics

Operation Performance
fork() <100ms @ 10K entities, <500ms @ 100K
commit() ~50ms without captureState
commit({ captureState: true }) ~150ms @ 1K entities, ~8s @ 1M entities
asOf() ~200ms @ 10K entities (rebuilds indexes)
Memory overhead (fork) 10-20% (shared storage)

See Also

Next: Import API →