Storage

SwirlDB provides pluggable storage through the DocumentStorage trait for current-state snapshots. CRDT change tracking is handled internally by Automerge.

Storage Architecture

Trait Definitions

All storage adapters implement these platform-agnostic traits:

trait DocumentStorage {
    async fn save(&self, key: &str, data: &[u8]) -> Result<()>;
    async fn load(&self, key: &str) -> Result<Option<Vec<u8>>>;
    async fn delete(&self, key: &str) -> Result<()>;
    async fn list_keys(&self) -> Result<Vec<String>>;
}

Browser Adapters

In-Memory (Default)

Volatile storage. Data is lost on page reload.

const db = await SwirlDB.create();

// Data lost on page reload
db.data.user.name = 'Alice';

Capacity: Limited by browser memory (~1-2GB)

Use case: Development, temporary sessions, caching

LocalStorage

Key-value storage with synchronous API. Data persists across sessions.

const db = await SwirlDB.withLocalStorage('my-app');

// Auto-persists to localStorage
db.data.user.settings = { theme: 'dark' };
await db.persist();

Capacity: ~5-10MB (browser-dependent)

Use case: Small apps, user preferences, offline-first PWAs

IndexedDB

Transactional database with asynchronous API. Supports structured data and binary blobs.

const db = await SwirlDB.withIndexedDB('my-app');

// Handles large datasets
db.data.documents = largeCollection;
await db.persist();

Capacity: ~50MB-1GB+ (browser quota system)

Use case: Large datasets, offline-first apps, media storage

Server Adapters

In-Memory (Multi-threaded)

Concurrent in-memory storage with thread-safe access.

use swirldb_server::storage::MemoryAdapter;

let storage = Arc::new(MemoryAdapter::new());
let db = SwirlDB::with_storage(storage, "db").await;

// Thread-safe, concurrent access
tokio::spawn(async move {
    db.set_path("counter", 1.into()).unwrap();
});

Capacity: Limited by server RAM

Use case: Caching, testing, ephemeral state

Redb (Embedded Database)

Embedded database with ACID guarantees and zero-copy reads.

use swirldb_server::storage::RedbAdapter;

let storage = RedbAdapter::new("./data/swirldb.redb")?;
let db = SwirlDB::with_storage(Arc::new(storage), "db").await;

// ACID transactions, crash-safe
db.set_path("user.profile", user_data)?;
db.persist().await?;  // Durable commit

Capacity: Limited by disk space

Use case: Production servers, durable state, embedded systems

SQLite (Planned)

SQL database with queryable schema. Supports relational queries alongside CRDT operations.

use swirldb_server::storage::SqliteAdapter;

let storage = SqliteAdapter::new("./data/swirldb.db")?;
let db = SwirlDB::with_storage(Arc::new(storage), "db").await;

// SQL queries + CRDT operations
let results = storage.query("SELECT * FROM documents WHERE created > ?", [timestamp])?;

Status: 🔜 Planned

S3 / Cloud Storage (Planned)

Object storage adapter. Compatible with AWS S3, Google Cloud Storage, Azure Blob, or S3-compatible backends.

use swirldb_server::storage::S3Adapter;

let storage = S3Adapter::new("my-bucket", "us-east-1")?;
let db = SwirlDB::with_storage(Arc::new(storage), "db").await;

db.persist().await?;  // Writes to S3

Status: 🔜 Planned

Integration with Encryption

Storage adapters integrate with encryption providers for at-rest protection.

Encrypted Storage Example

use swirldb_core::encryption::AesGcmProvider;
use swirldb_server::storage::RedbAdapter;

// Create encrypted storage
let encryption = AesGcmProvider::new_random();
let storage = RedbAdapter::new("./data")
    .with_encryption(encryption);

let db = SwirlDB::with_storage(Arc::new(storage), "db").await;

// All data encrypted before hitting disk
db.set_path("sensitive.data", secret_value)?;
db.persist().await?;  // Encrypted write to redb

Custom Storage Adapters

Implement the DocumentStorage trait to create custom backends for your specific needs.

Example: Redis Adapter

use swirldb_core::storage::{DocumentStorage, DocumentStorageMarker};
use async_trait::async_trait;

struct RedisStorage {
    client: redis::Client,
}

impl DocumentStorageMarker for RedisStorage {}

#[async_trait]
impl DocumentStorage for RedisStorage {
    async fn save(&self, key: &str, data: &[u8]) -> Result<()> {
        let mut conn = self.client.get_async_connection().await?;
        redis::cmd("SET")
            .arg(key)
            .arg(data)
            .query_async(&mut conn)
            .await?;
        Ok(())
    }

    async fn load(&self, key: &str) -> Result<Option<Vec<u8>>> {
        let mut conn = self.client.get_async_connection().await?;
        let result: Option<Vec<u8>> = redis::cmd("GET")
            .arg(key)
            .query_async(&mut conn)
            .await?;
        Ok(result)
    }

    // Implement delete, list_keys...
}

Performance Characteristics

Browser Storage Comparison

Adapter Capacity Speed API
In-Memory ~1-2GB Fastest Sync
LocalStorage ~5-10MB Fast Sync
IndexedDB ~50MB-1GB+ Moderate Async

Server Storage Comparison

Adapter Capacity Durability Use Case
In-Memory Limited by RAM ❌ Volatile Caching, testing
Redb Limited by disk ✅ ACID Production, embedded
SQLite Limited by disk ✅ ACID Queryable, portable