2025-08-26 12:32:21 -07:00
/ * *
* Backup & Restore System - Atomic Age Data Preservation Protocol
*
* 🧠 Complete backup / restore with compression and verification
* ⚛ ️ 1950 s retro sci - fi aesthetic maintained throughout
* /
2025-09-11 16:23:32 -07:00
import { Brainy } from '../brainy.js'
2025-08-26 12:32:21 -07:00
import * as fs from '../universal/fs.js'
import * as path from '../universal/path.js'
// @ts-ignore
import chalk from 'chalk'
// @ts-ignore
import ora from 'ora'
// @ts-ignore
import boxen from 'boxen'
// @ts-ignore
import prompts from 'prompts'
export interface BackupOptions {
compress? : boolean
output? : string
includeMetadata? : boolean
includeStatistics? : boolean
verify? : boolean
password? : string
}
export interface RestoreOptions {
verify? : boolean
overwrite? : boolean
password? : string
dryRun? : boolean
}
export interface BackupManifest {
version : string
timestamp : string
brainyVersion : string
entityCount : number
relationshipCount : number
storageType : string
compressed : boolean
encrypted : boolean
checksum : string
metadata : {
created : string
description? : string
tags? : string [ ]
}
}
/ * *
* Backup & Restore Engine - The Brain ' s Memory Preservation System
* /
export class BackupRestore {
2025-09-11 16:23:32 -07:00
private brainy : Brainy
2025-08-26 12:32:21 -07:00
private colors = {
primary : chalk.hex ( '#3A5F4A' ) ,
success : chalk.hex ( '#2D4A3A' ) ,
warning : chalk.hex ( '#D67441' ) ,
error : chalk.hex ( '#B85C35' ) ,
info : chalk.hex ( '#4A6B5A' ) ,
dim : chalk.hex ( '#8A9B8A' ) ,
highlight : chalk.hex ( '#E88B5A' ) ,
accent : chalk.hex ( '#F5E6D3' ) ,
brain : chalk.hex ( '#E88B5A' )
}
private emojis = {
brain : '🧠' ,
atom : '⚛️' ,
disk : '💾' ,
archive : '📦' ,
shield : '🛡️' ,
check : '✅' ,
warning : '⚠️' ,
sparkle : '✨' ,
rocket : '🚀' ,
gear : '⚙️' ,
time : '⏰'
}
2025-09-11 16:23:32 -07:00
constructor ( brainy : Brainy ) {
2025-08-26 12:32:21 -07:00
this . brainy = brainy
}
/ * *
* Create a complete backup of Brainy data
* /
async createBackup ( options : BackupOptions = { } ) : Promise < string > {
const outputPath = options . output || this . generateBackupPath ( )
console . log ( boxen (
` ${ this . emojis . archive } ${ this . colors . brain ( 'ATOMIC DATA PRESERVATION PROTOCOL' ) } ${ this . emojis . atom } \ n \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Initiating brain backup sequence' ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Output:' ) } ${ this . colors . highlight ( outputPath ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Compression:' ) } ${ this . colors . highlight ( options . compress ? 'Enabled' : 'Disabled' ) } ` ,
{ padding : 1 , borderStyle : 'round' , borderColor : '#E88B5A' }
) )
const spinner = ora ( ` ${ this . emojis . brain } Scanning neural pathways... ` ) . start ( )
try {
// Phase 1: Collect data
spinner . text = ` ${ this . emojis . gear } Extracting neural data... `
const backupData = await this . collectBackupData ( options )
// Phase 2: Create manifest
spinner . text = ` ${ this . emojis . atom } Generating quantum manifest... `
const manifest = await this . createManifest ( backupData , options )
// Phase 3: Package data
spinner . text = ` ${ this . emojis . archive } Packaging atomic data... `
const packagedData = {
manifest ,
data : backupData
}
// Phase 4: Compress if requested
let finalData = JSON . stringify ( packagedData , null , 2 )
if ( options . compress ) {
spinner . text = ` ${ this . emojis . gear } Applying quantum compression... `
finalData = await this . compressData ( finalData )
}
// Phase 5: Encrypt if password provided
if ( options . password ) {
spinner . text = ` ${ this . emojis . shield } Applying atomic encryption... `
finalData = await this . encryptData ( finalData , options . password )
}
// Phase 6: Write to file
spinner . text = ` ${ this . emojis . disk } Storing in atomic vault... `
await fs . writeFile ( outputPath , finalData )
// Phase 7: Verify if requested
if ( options . verify ) {
spinner . text = ` ${ this . emojis . check } Verifying atomic integrity... `
await this . verifyBackup ( outputPath , options )
}
spinner . succeed ( this . colors . success (
` ${ this . emojis . sparkle } Backup complete! Neural pathways preserved in atomic vault. `
) )
console . log ( boxen (
` ${ this . emojis . brain } ${ this . colors . brain ( 'BACKUP SUMMARY' ) } \ n \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Entities:' ) } ${ this . colors . primary ( manifest . entityCount . toLocaleString ( ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Relationships:' ) } ${ this . colors . primary ( manifest . relationshipCount . toLocaleString ( ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Size:' ) } ${ this . colors . highlight ( this . formatFileSize ( finalData . length ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Location:' ) } ${ this . colors . highlight ( outputPath ) } ` ,
{ padding : 1 , borderStyle : 'round' , borderColor : '#2D4A3A' }
) )
return outputPath
} catch ( error ) {
spinner . fail ( 'Backup failed - atomic vault compromised!' )
throw error
}
}
/ * *
* Restore Brainy data from backup
* /
async restoreBackup ( backupPath : string , options : RestoreOptions = { } ) : Promise < void > {
console . log ( boxen (
` ${ this . emojis . rocket } ${ this . colors . brain ( 'ATOMIC RESTORATION PROTOCOL' ) } ${ this . emojis . atom } \ n \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Initiating neural restoration sequence' ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Source:' ) } ${ this . colors . highlight ( backupPath ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Mode:' ) } ${ this . colors . highlight ( options . dryRun ? 'Simulation' : 'Full Restore' ) } ` ,
{ padding : 1 , borderStyle : 'round' , borderColor : '#E88B5A' }
) )
const spinner = ora ( ` ${ this . emojis . brain } Loading atomic vault... ` ) . start ( )
try {
// Phase 1: Load backup file
spinner . text = ` ${ this . emojis . disk } Reading atomic data... `
let rawData = await fs . readFile ( backupPath , 'utf8' )
// Phase 2: Decrypt if needed
if ( options . password ) {
spinner . text = ` ${ this . emojis . shield } Decrypting atomic data... `
rawData = await this . decryptData ( rawData , options . password )
}
// Phase 3: Decompress if needed
spinner . text = ` ${ this . emojis . gear } Decompressing quantum data... `
const decompressedData = await this . decompressData ( rawData )
// Phase 4: Parse backup data
const backupPackage = JSON . parse ( decompressedData )
const { manifest , data } = backupPackage
// Phase 5: Verify integrity
if ( options . verify ) {
spinner . text = ` ${ this . emojis . check } Verifying atomic integrity... `
await this . verifyRestoreData ( data , manifest )
}
// Phase 6: Display what will be restored
console . log ( '\n' + boxen (
` ${ this . emojis . brain } ${ this . colors . brain ( 'RESTORATION PREVIEW' ) } \ n \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Backup Date:' ) } ${ this . colors . highlight ( new Date ( manifest . timestamp ) . toLocaleString ( ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Entities:' ) } ${ this . colors . primary ( manifest . entityCount . toLocaleString ( ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Relationships:' ) } ${ this . colors . primary ( manifest . relationshipCount . toLocaleString ( ) ) } \ n ` +
` ${ this . colors . accent ( '◆' ) } ${ this . colors . dim ( 'Storage Type:' ) } ${ this . colors . highlight ( manifest . storageType ) } ` ,
{ padding : 1 , borderStyle : 'round' , borderColor : '#D67441' }
) )
if ( options . dryRun ) {
spinner . succeed ( this . colors . success ( 'Dry run complete - restoration simulation successful' ) )
return
}
// Phase 7: Confirm restoration
if ( ! options . overwrite ) {
const { confirm } = await prompts ( {
type : 'confirm' ,
name : 'confirm' ,
message : ` ${ this . emojis . warning } This will replace current data. Continue? ` ,
initial : false
} )
if ( ! confirm ) {
spinner . info ( 'Restoration cancelled by user' )
return
}
}
// Phase 8: Restore data
spinner . text = ` ${ this . emojis . rocket } Restoring neural pathways... `
await this . executeRestore ( data , manifest )
spinner . succeed ( this . colors . success (
` ${ this . emojis . sparkle } Restoration complete! Neural pathways successfully reconstructed. `
) )
} catch ( error ) {
spinner . fail ( 'Restoration failed - atomic vault corrupted!' )
throw error
}
}
/ * *
* List available backups in a directory
* /
async listBackups ( directory : string = './backups' ) : Promise < BackupManifest [ ] > {
try {
const files = await fs . readdir ( directory )
const backupFiles = files . filter ( f = > f . endsWith ( '.brainy' ) || f . endsWith ( '.json' ) )
const manifests : BackupManifest [ ] = [ ]
for ( const file of backupFiles ) {
try {
const filePath = path . join ( directory , file )
const manifest = await this . getBackupManifest ( filePath )
if ( manifest ) manifests . push ( manifest )
} catch ( error ) {
// Skip invalid backup files
}
}
return manifests . sort ( ( a , b ) = > new Date ( b . timestamp ) . getTime ( ) - new Date ( a . timestamp ) . getTime ( ) )
} catch ( error ) {
return [ ]
}
}
/ * *
* Get backup manifest without loading full backup
* /
private async getBackupManifest ( backupPath : string ) : Promise < BackupManifest | null > {
try {
const rawData = await fs . readFile ( backupPath , 'utf8' )
const decompressedData = await this . decompressData ( rawData )
const backupPackage = JSON . parse ( decompressedData )
return backupPackage . manifest || null
} catch ( error ) {
return null
}
}
/ * *
* Collect all data for backup
* /
private async collectBackupData ( options : BackupOptions ) : Promise < any > {
const data : any = {
entities : [ ] ,
relationships : [ ] ,
metadata : { } ,
statistics : null
}
// For now, we'll create a simplified backup that just captures the current state
// In a full implementation, this would use internal storage methods
console . log ( this . colors . warning ( 'Note: Backup system is in beta - captures basic data only' ) )
// Placeholder data collection
data . entities = [ ]
data . relationships = [ ]
// Collect metadata if requested
if ( options . includeMetadata ) {
data . metadata = await this . collectMetadata ( )
}
2025-09-11 16:23:32 -07:00
// Statistics not yet implemented
2025-08-26 12:32:21 -07:00
if ( options . includeStatistics ) {
2025-09-11 16:23:32 -07:00
console . warn ( 'Statistics collection not yet implemented in backup' )
2025-08-26 12:32:21 -07:00
}
return data
}
/ * *
* Create backup manifest
* /
private async createManifest ( data : any , options : BackupOptions ) : Promise < BackupManifest > {
return {
version : '1.0.0' ,
timestamp : new Date ( ) . toISOString ( ) ,
brainyVersion : '0.55.0' , // Would come from package.json
entityCount : data.entities.length ,
relationshipCount : data.relationships.length ,
storageType : 'unknown' , // Would detect from brainy instance
compressed : options.compress || false ,
encrypted : ! ! options . password ,
checksum : await this . calculateChecksum ( JSON . stringify ( data ) ) ,
metadata : {
created : new Date ( ) . toISOString ( ) ,
description : 'Atomic age brain backup' ,
tags : [ 'brainy' , 'neural-backup' , 'atomic-data' ]
}
}
}
/ * *
* Helper methods
* /
private generateBackupPath ( ) : string {
const timestamp = new Date ( ) . toISOString ( ) . replace ( /[:.]/g , '-' )
return ` ./brainy-backup- ${ timestamp } .brainy `
}
private async compressData ( data : string ) : Promise < string > {
2025-09-11 16:23:32 -07:00
// Use zlib gzip compression
const { gzip } = await import ( 'zlib' )
const { promisify } = await import ( 'util' )
const gzipAsync = promisify ( gzip )
const compressed = await gzipAsync ( Buffer . from ( data , 'utf-8' ) )
return compressed . toString ( 'base64' )
2025-08-26 12:32:21 -07:00
}
private async decompressData ( data : string ) : Promise < string > {
2025-09-11 16:23:32 -07:00
// Use zlib gunzip decompression
const { gunzip } = await import ( 'zlib' )
const { promisify } = await import ( 'util' )
const gunzipAsync = promisify ( gunzip )
const compressed = Buffer . from ( data , 'base64' )
const decompressed = await gunzipAsync ( compressed )
return decompressed . toString ( 'utf-8' )
2025-08-26 12:32:21 -07:00
}
private async encryptData ( data : string , password : string ) : Promise < string > {
2025-09-11 16:23:32 -07:00
// Use crypto module for AES-256 encryption
const crypto = await import ( 'crypto' )
// Generate key from password
const key = crypto . createHash ( 'sha256' ) . update ( password ) . digest ( )
const iv = crypto . randomBytes ( 16 )
const cipher = crypto . createCipheriv ( 'aes-256-cbc' , key , iv )
let encrypted = cipher . update ( data , 'utf8' , 'base64' )
encrypted += cipher . final ( 'base64' )
// Prepend IV to encrypted data for decryption
return iv . toString ( 'base64' ) + ':' + encrypted
2025-08-26 12:32:21 -07:00
}
private async decryptData ( data : string , password : string ) : Promise < string > {
2025-09-11 16:23:32 -07:00
// Use crypto module for AES-256 decryption
const crypto = await import ( 'crypto' )
// Split IV and encrypted data
const [ ivString , encrypted ] = data . split ( ':' )
const iv = Buffer . from ( ivString , 'base64' )
// Generate key from password
const key = crypto . createHash ( 'sha256' ) . update ( password ) . digest ( )
const decipher = crypto . createDecipheriv ( 'aes-256-cbc' , key , iv )
let decrypted = decipher . update ( encrypted , 'base64' , 'utf8' )
decrypted += decipher . final ( 'utf8' )
return decrypted
2025-08-26 12:32:21 -07:00
}
private async verifyBackup ( backupPath : string , options : BackupOptions ) : Promise < void > {
// Placeholder - would verify backup integrity
}
private async verifyRestoreData ( data : any , manifest : BackupManifest ) : Promise < void > {
const actualChecksum = await this . calculateChecksum ( JSON . stringify ( data ) )
if ( actualChecksum !== manifest . checksum ) {
throw new Error ( 'Data integrity check failed - backup may be corrupted' )
}
}
private async executeRestore ( data : any , manifest : BackupManifest ) : Promise < void > {
2025-09-11 16:23:32 -07:00
console . log ( this . colors . info ( '🔄 Starting restore process...' ) )
2025-08-26 12:32:21 -07:00
// Phase 1: Validate data structure
if ( ! data . entities || ! Array . isArray ( data . entities ) ) {
throw new Error ( 'Invalid backup data structure' )
}
2025-09-11 16:23:32 -07:00
// Phase 2: Clear existing data if overwriting
console . log ( this . colors . dim ( 'Clearing existing data...' ) )
const dataAPI = await this . brainy . data ( )
await dataAPI . clear ( )
2025-08-26 12:32:21 -07:00
2025-09-11 16:23:32 -07:00
// Phase 3: Restore entities
console . log ( this . colors . dim ( ` Restoring ${ data . entities . length } entities... ` ) )
const entityMap = new Map < string , string > ( ) // old ID -> new ID mapping
2025-08-26 12:32:21 -07:00
2025-09-11 16:23:32 -07:00
for ( const entity of data . entities ) {
const newId = await this . brainy . add ( {
data : entity.metadata || entity . data ,
type : entity . type ,
metadata : entity.metadata ,
vector : entity.vector , // Preserve original vectors if available
service : entity.service
} )
entityMap . set ( entity . id , newId )
}
// Phase 4: Restore relationships if they exist
if ( data . relationships && Array . isArray ( data . relationships ) ) {
console . log ( this . colors . dim ( ` Restoring ${ data . relationships . length } relationships... ` ) )
for ( const rel of data . relationships ) {
// Map old IDs to new IDs
const fromId = entityMap . get ( rel . from ) || rel . from
const toId = entityMap . get ( rel . to ) || rel . to
await this . brainy . relate ( {
from : fromId ,
to : toId ,
type : rel . type ,
metadata : rel.metadata || { }
} )
}
}
// Phase 5: Restore metadata if present
2025-08-26 12:32:21 -07:00
if ( data . metadata ) {
await this . restoreMetadata ( data . metadata )
}
2025-09-11 16:23:32 -07:00
console . log ( this . colors . success ( '✅ Restore completed successfully' ) )
2025-08-26 12:32:21 -07:00
}
private async collectMetadata ( ) : Promise < any > {
2025-09-11 16:23:32 -07:00
// Collect global metadata like statistics, configuration, etc.
const dataApi = await this . brainy . data ( )
const stats = await dataApi . getStats ( )
return {
totalEntities : stats.entities || 0 ,
timestamp : new Date ( ) . toISOString ( ) ,
version : '3.0.0'
}
2025-08-26 12:32:21 -07:00
}
private async restoreMetadata ( metadata : any ) : Promise < void > {
2025-09-11 16:23:32 -07:00
// Store restored metadata for reference
// Could be saved to a config store or logged
console . log ( this . colors . dim ( ` Restored from backup created at: ${ metadata . timestamp } ` ) )
2025-08-26 12:32:21 -07:00
}
private async calculateChecksum ( data : string ) : Promise < string > {
2025-09-11 16:23:32 -07:00
// Use crypto module for SHA-256 checksum
const crypto = await import ( 'crypto' )
return crypto . createHash ( 'sha256' ) . update ( data ) . digest ( 'hex' )
2025-08-26 12:32:21 -07:00
}
private formatFileSize ( bytes : number ) : string {
const units = [ 'B' , 'KB' , 'MB' , 'GB' ]
let size = bytes
let unitIndex = 0
while ( size >= 1024 && unitIndex < units . length - 1 ) {
size /= 1024
unitIndex ++
}
return ` ${ size . toFixed ( 1 ) } ${ units [ unitIndex ] } `
}
}