rag-module 0.6.7

Enterprise RAG module with chat context storage, vector search, session management, and model downloading. Rust implementation with Node.js compatibility.
//! View Encrypted Chat Data Example
//!
//! This example demonstrates how chat history data is stored and encrypted.
//! It shows both the encrypted format and how to properly decrypt it.
//!
//! Usage:
//! ```bash
//! # First create sample data
//! cargo run --example show_chat_storage_format
//!
//! # Then view the encrypted data
//! cargo run --example view_encrypted_chat_data
//! ```

use anyhow::{Result, Context};
use std::fs;
use std::path::Path;

#[tokio::main]
async fn main() -> Result<()> {
    println!("\n╔══════════════════════════════════════════════════════════════╗");
    println!("║      View Encrypted Chat Data & Decryption Example          ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    let data_path = "./example-enhanced-chat-data";
    let file_path = format!("{}/qdrant-data/cudewvsbxakj1n/chat_history-documents.json", data_path);

    println!("📂 Data Directory: {}", data_path);
    println!("📄 Chat File: {}\n", file_path);

    // Check if file exists
    if !Path::new(&file_path).exists() {
        println!("❌ Chat history file not found!");
        println!("\n💡 To create sample data, run:");
        println!("   cargo run --example show_chat_storage_format\n");
        return Ok(());
    }

    // Read the file as raw content
    let file_content = fs::read_to_string(&file_path)
        .context("Failed to read chat history file")?;

    println!("╔══════════════════════════════════════════════════════════════╗");
    println!("║  RAW ENCRYPTED FILE CONTENTS (EXACT JSON FORMAT)             ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    let total_lines = file_content.lines().filter(|l| !l.trim().is_empty()).count();
    println!("File contains {} documents\n", total_lines);

    // Display each line as-is with line numbers
    for (i, line) in file_content.lines().enumerate() {
        if line.trim().is_empty() {
            continue;
        }

        println!("─────────────────────────────────────────────────────────────");
        println!("Document #{}", i + 1);
        println!("─────────────────────────────────────────────────────────────");

        // Try to pretty-print if it's valid JSON
        match serde_json::from_str::<serde_json::Value>(line) {
            Ok(json) => {
                println!("{}\n", serde_json::to_string_pretty(&json)?);
            }
            Err(_) => {
                // If not valid JSON, just print the raw line
                println!("{}\n", line);
            }
        }
    }

    // Initialize RAG module for decryption
    println!("\n╔══════════════════════════════════════════════════════════════╗");
    println!("║  DECRYPTED DATA (ALL CONVERSATIONS)                         ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    use rag_module::RagModule;

    let rag = RagModule::new(data_path).await?;
    rag.initialize().await?;

    println!("✅ Decryption service initialized\n");
    println!("🔓 Decrypting all chat history data...\n");

    // Use get_decrypted_chat_history to get ALL data organized by context_id
    match rag.get_decrypted_chat_history("cudewvsbxakj1n").await {
        Ok(decrypted_data) => {
            println!("╔══════════════════════════════════════════════════════════════╗");
            println!("║  COMPLETE DECRYPTED DATA (EXACT JSON FORMAT)                ║");
            println!("╚══════════════════════════════════════════════════════════════╝\n");

            println!("{}\n", serde_json::to_string_pretty(&decrypted_data)?);
        }
        Err(e) => {
            println!("❌ Decryption failed: {}\n", e);
        }
    }

    println!("╔══════════════════════════════════════════════════════════════╗");
    println!("║  ENCRYPTION DETAILS                                          ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    println!("Encryption Scheme: AES-256-GCM");
    println!("Key Length: 256 bits (32 bytes)");
    println!("Nonce Length: 96 bits (12 bytes)");
    println!("Tag Length: 128 bits (16 bytes)\n");

    println!("Key Storage:");
    println!("  Primary: macOS Keychain (OS-level security)");
    println!("  Backup:  {}/keys/ directory (encrypted)", data_path);
    println!("  Keys:    main_key, embedding_key, sync_key\n");

    println!("Encrypted Content Format:");
    println!("  Base64 encoded string containing:");
    println!("    - Nonce (12 bytes)");
    println!("    - Ciphertext (variable length)");
    println!("    - Authentication tag (16 bytes)\n");

    println!("Decryption Process:");
    println!("  1. Retrieve encryption key from keychain");
    println!("  2. Decode Base64 encrypted content");
    println!("  3. Extract nonce, ciphertext, and tag");
    println!("  4. Decrypt using AES-256-GCM");
    println!("  5. Verify authentication tag");
    println!("  6. Return plaintext message\n");

    println!("✅ Example completed!");
    println!("\n📚 Summary:");
    println!("  • Encrypted data is stored in: {}", file_path);
    println!("  • Each message is encrypted separately");
    println!("  • Decryption is automatic when using search_chat_history()");
    println!("  • Encryption keys are securely stored in macOS Keychain\n");

    Ok(())
}