SynesthesiaDev.Nocturne.Database 2026.707.1

dotnet add package SynesthesiaDev.Nocturne.Database --version 2026.707.1
                    
NuGet\Install-Package SynesthesiaDev.Nocturne.Database -Version 2026.707.1
                    
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="SynesthesiaDev.Nocturne.Database" Version="2026.707.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SynesthesiaDev.Nocturne.Database" Version="2026.707.1" />
                    
Directory.Packages.props
<PackageReference Include="SynesthesiaDev.Nocturne.Database" />
                    
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 SynesthesiaDev.Nocturne.Database --version 2026.707.1
                    
#r "nuget: SynesthesiaDev.Nocturne.Database, 2026.707.1"
                    
#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 SynesthesiaDev.Nocturne.Database@2026.707.1
                    
#: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=SynesthesiaDev.Nocturne.Database&version=2026.707.1
                    
Install as a Cake Addin
#tool nuget:?package=SynesthesiaDev.Nocturne.Database&version=2026.707.1
                    
Install as a Cake Tool

🌙 Nocturne

GitHub Workflow Status NuGet Version

Target .NET License

Lightweight easy-to-use local database for C#

No reflection, no hidden native wrapper, no magic


Usage

Create one global database object

public static readonly NocturneDatabase NOCTURNE_DATABASE = new NocturneDatabase
{
    FilePath = "./data/database.nocturne",
    CompactOnLaunch = true,
    AutomaticallyCompact = false
};

In all of your "models" you then define a collection object:


public record Person(string Name, int Age, bool IsCool) 
{
    public static readonly NocturneCollection<string, Person> DB_COLLECTION = Program.NOCTURNE_DATABASE.For(
        collectionKey: "people",
        schemaVersion: 1,
        keySerializer: KeySerializers.STRING,
        valueSerializer: DATABASE_SERIALIZER
    );
}

Every model needs their own serializer. Nocturne does no do any source generation or reflection by design so you will need to provide your own serializer.

You can either write one manually:

public static readonly INocturneSerializer<Person> DATABASE_SERIALIZER = NocturneSerializer.For<Person>
(
    (buffer) =>
    {
        var name = buffer.ReadString();
        var age = buffer.ReadInt();
        var isCool = buffer.ReadBoolean();
        return new Person(name, age, isCool);
    },
    (buffer, person) =>
    {
        buffer.WriteString(person.Name);
        buffer.WriteInt(person.Age);
        buffer.WriteBoolean(person.IsCool);
    }
);

Ore uses an IBinaryCodec which is from Codon library that comes packaged in:

public static readonly IBinaryCodec<Person> CODEC = BinaryCodecs.For<Person>()
    .Field(BinaryCodecs.STRING, p => p.Name)
    .Field(BinaryCodecs.INT, p => p.Age)
    .Field(BinaryCodecs.BOOLEAN, p => p.IsCool)
    .Build((name, age, cool) => new Person(name, age, cool));

public static readonly INocturneSerializer<Person> DATABASE_SERIALIZER = NocturneSerializer.FromCodec(CODEC);

Collections

You can then do all the fun stuff like Insert, Find, FindOrNull, FindOrAdd, Delete, Nuke, etc. by calling Person.DB_COLLECTION:

var newPerson = new Person("John Person", 21, false);
Person.DB_COLLECTION.Insert("john", newPerson);

var john = Person.DB_COLLECTION.Find("john");
Person.DB_COLLECTION.Delete("john");

//fuck it, who needs prod anyway
Person.DB_COLLECTION.Nuke();

Schema Migrations

If you change your model structure, Nocturne handles step-by-step migrations using byte buffers. For example, migrating from Version 0 (which had a FunFact string) to Version 1 (current):


IMigrationStrategy.Migrations()
    .Add(0, buffer =>
    {
        // 1. Read the old schema format out of the buffer
        var name = BinaryCodecs.STRING.Read(buffer);
        var age = BinaryCodecs.INT.Read(buffer);
        var isCool = BinaryCodecs.BOOLEAN.Read(buffer);
        var _ = BinaryCodecs.STRING.Read(buffer); // Discard old "FunFact" string

        // 2. Write the clean data into the new schema format
        var newBuffer = Unpooled.Buffer();
        BinaryCodecs.STRING.Write(newBuffer, name);
        BinaryCodecs.INT.Write(newBuffer, age);
        BinaryCodecs.BOOLEAN.Write(newBuffer, isCool);

        return newBuffer; // buffer is automatically released if you choose to use pooled one
    })
    .Build()

You may also use IMigrationStrategy.DeleteIfMigrationRequired() which deletes the collection and creates new one with new schema if a migration is required


Compaction

This is a log-style database, meaning it's append-only. Records do not get truly deleted from the database file, only marked as dead. Calling Database.Compact() will remove all records marked as dead or outdated and compact the file down. Remember to call it once in a while, otherwise your database will grow exponentially!

There are properties CompactOnLaunch which compacts the database file when the database launches and AutomaticallyCompact which compacts the database automatically when over 50% of its content is dead/outdated. Do keep in mind compacting does block the main thread and may take a good 500ms (or more depending on your data size)

If you are working with performance-critical stuff, I recommend managing compactions manually instead of relying on AutomaticallyCompact


Backups

You may take a backup of the database by selecting the file and pressing CTRL + C followed by CTRL + V and adding "dkjglkg.bak" at the end of the file name

(pro tip: if the file name is already taken, smash your keyboard again. Repeat until you land on not taken file name)

(ultra pro tip: if you are a real pro, you may even set up a CRON job for this!)


Lastly, this is a learning project I made practically in one sitting. You should probably not use it in production code...

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.707.1 47 7/7/2026
2026.707.0 41 7/7/2026
2026.706.0 59 7/6/2026