AcornDB 0.4.0

dotnet add package AcornDB --version 0.4.0
                    
NuGet\Install-Package AcornDB -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AcornDB" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AcornDB" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="AcornDB" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AcornDB --version 0.4.0
                    
#r "nuget: AcornDB, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AcornDB@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AcornDB&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=AcornDB&version=0.4.0
                    
Install as a Cake Tool

🌰 AcornDB

AcornDB logo

AcornDB is a lightweight, reactive, embedded database for .NET β€” for devs who’d rather ship products than pay $400/month to store 5MB of JSON.

🐿️ Nutty by design. Practical by necessity.


πŸš€ Why AcornDB Exists

Most apps don’t need Cosmos DB, Kafka, or Redis.

They need:

  • Fast, local-first persistence
  • Simple per-tenant or per-user storage
  • Offline support + syncing that doesn’t make you cry

AcornDB is for:

  • Desktop apps
  • IoT devices
  • Mobile backends
  • CLI tools
  • Serverless & edge workloads
  • And yes β€” you with the single-user SaaS that stores 10KB per user

🍁 Core Concepts

Term What It Means
Tree<T> A local document collection β€” your "embedded table"
Nut<T> An object wrapped with metadata (TTL, version, timestamp)
ITrunk<T> Storage abstraction: File, Memory, Azure Blob, or versioned
Branch A connection to a remote Tree via HTTP
Tangle A live sync session between two Trees
Grove A set of Trees managed + synced together
Canopy (Internal) sync orchestrator living inside the Grove
Stash/Crack/Toss Insert, read, and delete objects β€” squirrel-style verbs
Shake() Manual sync trigger

πŸ”§ Features

Feature Description
🌰 Stash, Crack, Toss Drop-in persistence with zero boilerplate
🎯 Auto-ID Detection Stash without explicit IDs - automatically uses Id or Key properties
πŸ›‘οΈ Nut<T> Versioned, timestamped, TTL-wrapped records
πŸ” Branch, Tangle, Grove Live sync between Trees, across machines
πŸͺ’ Entangle<T>() Sync trees via HTTP or in-process without a server
🎩 Oversee<T>() One-liner to monitor remote branches
βš–οΈ Squabble() + Judge Built-in conflict resolution with custom override
🧠 INutment<TKey> Typed ID interface for strongly keyed documents
🧹 SmushNow() Manual compaction of log-based storage
πŸ›°οΈ ExportChanges() / ImportChanges() Manual sync if you're old-school
🌲 Grove.Plant<T>() Auto-creates and registers a Tree<T>
πŸ” Totem-based auth (coming) Because why not woodland-themed security?

πŸ§ͺ Getting Started

# Coming soon to NuGet:
dotnet add package AcornDB

Quick Example

// Define your model with an Id property
public class User
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public string Name { get; set; }
}

// Create a Tree (defaults to FileTrunk - no config needed!)
var tree = new Tree<User>();

// Stash without explicit ID (auto-detected from Id property)
tree.Stash(new User { Name = "Squirrelius Maximus" });

// Crack it back
var user = tree.Crack("...");

With Sync

// Set up syncing with a Grove
var grove = new Grove();
grove.Plant(tree);
grove.Oversee<User>(new Branch("http://localhost:5000")); // auto-sync!

tree.Shake(); // optionally force a sync

✨ What Makes AcornDB Simple

🎯 Auto-ID Detection

No more specifying IDs twice. If your model has an Id or Key property, AcornDB finds it automatically:

public class Task
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public string Title { get; set; }
}

var tree = new Tree<Task>();
tree.Stash(new Task { Title = "Learn AcornDB" }); // ID auto-detected!

πŸͺ΅ Optional Trunk (Defaults to FileTrunk)

Skip the boilerplate. Trees default to file-based storage:

// Before
var tree = new Tree<User>(new FileTrunk<User>("data/User"));

// Now
var tree = new Tree<User>(); // Automatically uses FileTrunk!

πŸͺ’ In-Process Sync

Connect trees directly without HTTP:

var tree1 = new Tree<User>();
var tree2 = new Tree<User>(new MemoryTrunk<User>());

tree1.Entangle(tree2); // No server needed!

πŸ“ Shared Storage Sync

Multiple processes? Just point to the same directory:

// Process 1
var tree1 = new Tree<User>(new FileTrunk<User>("shared/data"));
tree1.Stash(new User { Id = "alice", Name = "Alice" });

// Process 2
var tree2 = new Tree<User>(new FileTrunk<User>("shared/data"));
var alice = tree2.Crack("alice"); // Already there!

πŸ—„οΈ Storage Abstraction (Trunks)

AcornDB uses Trunks to abstract storage β€” swap your backend without touching your Tree code.

Available Trunks

Trunk Use Case History Sync
FileTrunk<T> Simple file-based storage ❌ βœ…
MemoryTrunk<T> Fast in-memory (great for tests) ❌ βœ…
DocumentStoreTrunk<T> Full versioning & time-travel βœ… βœ…
AzureTrunk<T> Azure Blob Storage ❌ βœ…

Examples

// πŸ“ FileTrunk (DEFAULT): Simple, no history
var fileTree = new Tree<User>(); // Defaults to FileTrunk!
fileTree.Stash(new User { Id = "alice", Name = "Alice" }); // Auto-ID

// Or explicit path
var customTree = new Tree<User>(new FileTrunk<User>("data/users"));

// πŸ’Ύ MemoryTrunk: Fast, non-durable
var memTree = new Tree<User>(new MemoryTrunk<User>());
memTree.Stash(new User { Id = "bob", Name = "Bob" });

// πŸ“š DocumentStoreTrunk: Full history & versioning
var docTree = new Tree<User>(new DocumentStoreTrunk<User>("data/versioned"));
docTree.Stash(new User { Id = "charlie", Name = "Charlie v1" });
docTree.Stash(new User { Id = "charlie", Name = "Charlie v2" });

var history = docTree.GetHistory("charlie"); // Get previous versions!
// Returns: 1 previous version ("Charlie v1")

// πŸ”„ Export/Import between trunks
var sourceTrunk = new FileTrunk<User>("data/source");
var targetTrunk = new AzureTrunk<User>("connection-string");

targetTrunk.ImportChanges(sourceTrunk.ExportChanges()); // Migrate!

Time-Travel with DocumentStoreTrunk

var tree = new Tree<Product>(new DocumentStoreTrunk<Product>("data/products"));

tree.Stash(new Product { Id = "widget", Name = "Widget v1.0" });
tree.Stash(new Product { Id = "widget", Name = "Widget v2.0" });
tree.Stash(new Product { Id = "widget", Name = "Widget v3.0" });

var current = tree.Crack("widget");     // "Widget v3.0"
var history = tree.GetHistory("widget"); // ["Widget v1.0", "Widget v2.0"]

// All changes stored in append-only log: data/products/changes.log

NotSupportedException Pattern

Trunks that don't support history throw NotSupportedException:

var memTree = new Tree<User>(new MemoryTrunk<User>());
try {
    var history = memTree.GetHistory("user1");
} catch (NotSupportedException) {
    Console.WriteLine("MemoryTrunk doesn't support history!");
}

Feature Detection with ITrunkCapabilities

Check trunk features without exceptions:

var trunk = new MemoryTrunk<User>();
var caps = trunk.GetCapabilities();

Console.WriteLine($"Trunk: {caps.TrunkType}");
Console.WriteLine($"History: {caps.SupportsHistory}");
Console.WriteLine($"Sync: {caps.SupportsSync}");
Console.WriteLine($"Durable: {caps.IsDurable}");
Console.WriteLine($"Async: {caps.SupportsAsync}");

// Use extension methods for quick checks
if (trunk.CanGetHistory())
{
    var history = trunk.GetHistory("user1");
}
else
{
    Console.WriteLine("This trunk doesn't support history");
}

Capability Matrix:

Trunk History Sync Durable Async
FileTrunk ❌ βœ… βœ… ❌
MemoryTrunk ❌ βœ… ❌ ❌
DocumentStoreTrunk βœ… βœ… βœ… ❌
AzureTrunk ❌ βœ… βœ… βœ…

🌐 Sync with TreeBark

TreeBark is the HTTP sync server for AcornDB - it exposes Trees over REST endpoints.

Quick Start Sync Server

// Server side (AcornSyncServer project)
var grove = new Grove();
grove.Plant(new Tree<User>(new FileTrunk<User>("data/users")));
grove.Plant(new Tree<Product>(new FileTrunk<Product>("data/products")));

// Run with: dotnet run --project AcornSyncServer
// TreeBark starts on http://localhost:5000

TreeBark REST API

Endpoint Method Description
/ GET Health check + API docs
/bark/{treeName}/stash POST Stash a nut to remote tree
/bark/{treeName}/crack/{id} GET Crack a nut from remote tree
/bark/{treeName}/toss/{id} DELETE Toss a nut from remote tree
/bark/{treeName}/export GET Export all nuts from tree
/bark/{treeName}/import POST Import nuts into tree

Client-Side Sync with Branch

// Client side - connect to remote TreeBark server
var localTree = new Tree<User>(new MemoryTrunk<User>());
var branch = new Branch("http://localhost:5000/bark/User");

// Manual push with auto-ID
var alice = new User { Id = "alice", Name = "Alice" };
localTree.Stash(alice);
branch.TryPush(alice.Id, localTree.Crack(alice.Id));

// Manual pull
await branch.ShakeAsync(localTree); // Pulls all remote changes

// Auto-sync with Tangle
var grove = new Grove();
grove.Plant(localTree);
grove.Entangle<User>(branch, "sync-session-1"); // Auto-syncs on every stash!

Full Sync Example

Server (dotnet run --project AcornSyncServer):

var grove = new Grove();
grove.Plant(new Tree<User>(new DocumentStoreTrunk<User>("data/users")));
// TreeBark running on http://localhost:5000

Client 1 (Desktop App):

var tree1 = new Tree<User>(new FileTrunk<User>("client1/users"));
var branch = new Branch("http://localhost:5000");

var alice = new User { Id = "alice", Name = "Alice" };
tree1.Stash(alice); // Auto-ID detection
branch.TryPush(alice.Id, tree1.Crack(alice.Id)); // Syncs to server

Client 2 (Mobile App):

var tree2 = new Tree<User>(new MemoryTrunk<User>());
var branch = new Branch("http://localhost:5000");

await branch.ShakeAsync(tree2); // Pulls "alice" from server!
var alice = tree2.Crack("alice"); // "Alice" is now local

🌰 AcornDB Visualizer - Web UI

Explore your Grove with an interactive web dashboard!

cd AcornVisualizer
dotnet run
# Open browser to http://localhost:5100

Features:

  • πŸ“Š Live Dashboard - Real-time stats on trees, nuts, and operations
  • 🌳 Tree Explorer - Browse all trees with detailed metadata
  • πŸ“ˆ Graph View - Interactive circular node visualization
  • πŸ” Nut Inspector - View payloads, timestamps, and history
  • βš™οΈ Trunk Info - See capabilities (history, sync, durable, async)
  • πŸ”„ Auto-Refresh - Updates every 5 seconds

Perfect for:

  • Local development and debugging
  • Visual demos and presentations
  • Understanding your grove structure
  • Monitoring nut operations

See AcornVisualizer/README.md for full documentation.


🌲 Same-Host Sync (No Server Required!)

For processes on the same host, AcornDB offers two simple sync strategies:

βœ… Option 1: Shared FileTrunk (Simplest!)

Just point both trees to the same directory:

// Process 1
var tree1 = new Tree<User>(new FileTrunk<User>("shared/users"));
tree1.Stash(new User { Id = "alice", Name = "Alice" });

// Process 2
var tree2 = new Tree<User>(new FileTrunk<User>("shared/users"));
var alice = tree2.Crack("alice"); // βœ… Automatically synced!

Zero setup. Zero config. Just works.


πŸͺ’ Option 2: In-Process Tree Entanglement

Sync two trees directly without HTTP:

var tree1 = new Tree<User>();
var tree2 = new Tree<User>(new MemoryTrunk<User>());

tree1.Entangle(tree2); // Direct tree-to-tree sync!

tree1.Stash(new User { Id = "bob", Name = "Bob" });
// Automatically synced to tree2 via InProcessBranch

Perfect for in-memory scenarios or testing.


When to Use Each Sync Strategy

Scenario Recommended Approach
Same host, multiple processes 🟒 Shared FileTrunk
Same process, different trees 🟒 In-Process Entanglement
Different hosts 🟒 TreeBark HTTP
Desktop apps with multiple instances 🟒 Shared FileTrunk
Mobile to cloud 🟒 TreeBark HTTP
Distributed systems 🟒 TreeBark HTTP
CLI tools 🟒 Shared FileTrunk
Complex multi-process with separate storage 🟒 File System Sync Hub

🧱 Project Structure

Folder Purpose
AcornDB Core engine (Tree, Nut, Trunk, Tangle)
AcornSyncServer TreeBark: HTTP sync server (REST API)
AcornVisualizer Web UI: Interactive grove dashboard
AcornDB.Canopy SignalR hub + visualizations
AcornDB.Demo Examples showcasing all features
SyncDemo Live multi-client sync demonstration
AcornDB.Test xUnit tests (26 passing)

πŸ§™ What's Coming

  • πŸ” Auth: Totems, ForageRights, Critters, and BarkCodes
  • πŸ“‘ Mesh sync: Peer-to-peer Tangle networks
  • πŸ“¦ NuGet & CLI: Install and create projects with acorn new
  • πŸ” AutoRecovery: Offline-first sync queue with resilience
  • πŸ§ͺ Playgrounds: Sample apps, code snippets, and demos
  • 🎨 Visualizer Enhancements: Real-time updates, diff viewer, dark mode


🌲 The Acorn Ethos

🐿️ Serious software. Zero seriousness.

AcornDB was born out of frustration with bloated infra, soulless APIs, and naming things like DataClientServiceManagerFactoryFactory.

So we built something better β€” not just in function, but in vibe.

We believe:

  • Developers deserve fun.
  • Tools should make you smile, not sigh.
  • Syncing JSON should not require Kubernetes and a degree in wizardry.
  • "Toss the nut and shake the tree" should be valid engineering advice.

If you’ve ever rage-quit YAML, yelled at Terraform, or cried syncing offline-first apps β€”
welcome. You’ve found your grove.

🌰 Stash boldly. Crack with confidence. And never, ever apologize for getting a little squirrelly.

🦦 Built with acorns and sarcasm

We’re tired of YAML. Tired of cloud bills. Tired of DataServiceFactoryClientFactoryFactory.

So we built AcornDB.

If you fork this, star it, or build something fun β€” send us your weirdest squirrel pun.


🐿️ Stay nutty.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on AcornDB:

Package Downloads
AcornDB.Canopy

🌲 AcornDB.Canopy - SignalR hub and real-time visualization extensions for AcornDB. Adds Hardwood HTTP server, live sync orchestration, and real-time grove monitoring capabilities.

AcornDB.Persistence.Cloud

🌰 Cloud storage providers for AcornDB - AWS S3, Azure Blob Storage, and other cloud-based trunk implementations. Separate package to keep core AcornDB lean.

AcornDB.Persistence.RDBMS

🌰 RDBMS storage providers for AcornDB - SQLite, SQL Server, PostgreSQL, MySQL trunk implementations for relational database persistence.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 183 10/7/2025
0.3.0 159 10/7/2025
0.1.0 173 10/6/2025

v0.4.0: NEW GitHubTrunk (Git-as-database with full version history), Nursery system for dynamic trunk discovery, trunk factory pattern, fluent trunk configuration API. Separate persistence packages: AcornDB.Persistence.Cloud (S3, Azure Blob) and AcornDB.Persistence.RDBMS (SQLite, SQL Server, PostgreSQL, MySQL).