segment-buffer 0.3.0

Durable bounded queue with zstd+CBOR segment files, ack-based deletion, and crash recovery
Documentation
//! Encrypted segment-buffer: segments are encrypted at rest with AES-256-GCM.
//!
//! Run with: `cargo run --example encrypted --features encryption`

// SegmentConfig is #[non_exhaustive]: Default + field reassignment is the only
// external construction pattern; accept the clippy lint for that reason.
#![allow(clippy::field_reassign_with_default)]

use segment_buffer::{AesGcmCipher, SegmentBuffer, SegmentConfig};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct SecretRecord {
    id: u64,
    payload: String,
}

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    // In production, load this from a secure key store (file, KMS, env var).
    let key = [0x42u8; 32];

    let tmp = tempfile::tempdir()?;

    let mut config = SegmentConfig::default();
    config.max_batch_events = 256;
    config.flush_interval_secs = 5;
    config.max_size_bytes = 1024 * 1024;
    config.compression_level = 3;
    config.cipher = Some(Box::new(AesGcmCipher::new(&key)));

    let buffer = SegmentBuffer::<SecretRecord>::open(tmp.path(), config)?;

    let record = SecretRecord {
        id: 1,
        payload: "classified".into(),
    };
    buffer.append(record.clone())?;
    buffer.flush()?;

    // The on-disk file is NOT plaintext — it's nonce + AES-GCM ciphertext.
    let entries = std::fs::read_dir(tmp.path())?;
    for entry in entries {
        let path = entry?.path();
        if path.extension().is_some_and(|ext| ext == "zst") {
            let bytes = std::fs::read(&path)?;
            println!(
                "Segment {:?}: {} bytes (encrypted)",
                path.file_name(),
                bytes.len()
            );
            assert!(!bytes.windows(8).any(|w| w == b"classified"));
        }
    }

    // But we can still read it back with the key.
    let recovered = buffer.read_from(0, 100)?;
    assert_eq!(recovered.len(), 1);
    assert_eq!(recovered[0], record);
    println!("Decrypted {} record successfully", recovered.len());

    Ok(())
}