Skip to main content

Kanta database

Kanta is a small embedded NoSQL store for async Python apps. It keeps live state in memory, writes transactional diffs to an append-only log, and supports versioned schema migrations.

Why Kanta

  • Fast synchronous reads and modifications on native Python objects
  • Durable writes with append-only file and periodic snapshots
  • Transaction semantics with rollback on failure
  • Explicit schema evolution via migrate_vN functions
  • Line-based JSON or binary MessagePack, or bring your own serializer
  • Even in JSON we can use non-string keys, bytes, datetimes, UUID and other types

This design is often preferable when you want low-latency local persistence without operating a separate database service. You get straightforward deployment, auditable history, and deterministic replay while keeping application state ergonomic to work with.

Queries and updates on native data are far faster than over an SQL server connection, and we can can provide fully synchronous operation. The limitation is that you can only use the same database within a single process at a time, but this is well suited for async programming.

Quick Start

import asyncio
import msgspec
from uuid import UUID, uuid7

from kanta import Kanta


class User(msgspec.Struct):
    name: str = ""
    email: str | None = None


class Data(msgspec.Struct):
    users: dict[UUID, User] = {}


async def main() -> None:
    async with Kanta("data.kantadb", Data()) as kanta:
        user_id = uuid7()
        with kanta.transaction(action="create_user") as data:
            data.users[user_id] = User(name="Alice")


asyncio.run(main())

Core Concepts

  1. Define your schema as a msgspec.Struct root object.
  2. Mutate data inside with kanta.transaction(...):.
  3. Let Kanta flush queued changes to disk in the background.
  4. Use snapshots and replay for fast startup and full history.

Bootstrap and Open Modes

When open() creates a brand-new database, it always writes a single bootstrap change record from the initial data object you passed to Kanta(...). The simplest bootstrap is therefore the object itself — no extra code is required.

Bootstrap handlers are optional. Use them only when you need to modify the initial state at creation time, for example to seed defaults or perform expensive/external setup that should happen exactly once:

kanta = Kanta("data.kantadb", Data())

@kanta.bootstrap(action="seed", user="system")
def seed_defaults(data) -> None:
    data.users["admin"] = User(name="Admin")

await kanta.open()

You can also use @kanta.bootstrap with no arguments and async handlers:

@kanta.bootstrap
async def bootstrap_async(data) -> None:
    data.counter = 1

Whether or not handlers are registered, exactly one bootstrap change record is written when a new database is created. The record contains the initial object, or the state after all bootstrap handlers have run. When handlers are present:

  • they run in registration order,
  • bootstrap metadata (action, user, mtime) is taken from the last registration.

If any bootstrap handler raises, Kanta closes and removes the database file, then re-raises the error.

open() also supports strict open mode:

await kanta.open(create=False)

With create=False, open fails if the database file does not exist or is empty.

Read-only mode opens an existing database without locking it or starting the background flush task. This is useful for readers that must not block the writer or modify the file:

await kanta.open(readonly=True)

In read-only mode, records are replayed and migrations are applied in memory, but transactions and explicit flushes are rejected and the file is never created if missing.

Fatal Error Handlers

Fatal background write errors can be observed with a decorator:

import os
import signal

@kanta.fatal_error
async def on_fatal(err):
    os.kill(os.getpid(), signal.SIGTERM)  # Die

Multiple fatal handlers are supported and run in registration order.

Migrations

Adding or removing a field and other such simple operations are automatic, but when the time comes to really change your data model, implement a migrate_v1 function that converts your old data to the new form. This works on plain built-in dict and other types, to avoid needing to preserve old versions of your structs.

Pass a module (or import path) containing migrate_vN functions:

kanta = Kanta("data.kantadb", Data(), migrations="myapp.migrations")
await kanta.open()

Kanta tracks migration version metadata automatically, and fast forwards your database to current version by running all the migrations needed while opening the database.

On-Disk Format

Kanta in JSON mode (default) stores newline-delimited records. Transaction history is viewable by any simple text editor, and rollbacks to prior state are done by simply removing final lines (one per transaction)

MsgPack mode uses binary records with length and checksum to avoid data corruption.

  • Change line: JSON object with metadata + diff
  • Snapshot line: SNAPSHOT { ... full state ... }

See docs/database.md for format details and invariants.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kanta-0.8.0.tar.gz (60.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kanta-0.8.0-py3-none-any.whl (51.3 kB view details)

Uploaded Python 3

File details

Details for the file kanta-0.8.0.tar.gz.

File metadata

  • Download URL: kanta-0.8.0.tar.gz
  • Upload date:
  • Size: 60.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for kanta-0.8.0.tar.gz
Algorithm Hash digest
SHA256 5f16d285a4976aad876afd09ebd5a67c8a1042dc30e5d72d1a3e9af3ec6e1cf6
MD5 eca8ed38c4ea44e7bcae4fbd419dc7bc
BLAKE2b-256 2ce2b5332b1f40d2b5dc222d285c2a22127e5f1aab2df2da49bcc12be6d651d5

See more details on using hashes here.

File details

Details for the file kanta-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: kanta-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 51.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for kanta-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9608bd62658dd35a48063809856dbba7412b88aace94a77ed9c274ba15bf1180
MD5 1540e3e79268e8f1d444d036e9ba32db
BLAKE2b-256 f7cb3d67f87af83621a72ebb3247eec400e2f8831c4d30a2133163f804f75170

See more details on using hashes here.

Release history Release notifications | RSS feed

0.9.1

2 files

0.9.0

2 files

0.8.2

2 files

0.8.1

2 files

This release

0.8.0 This release

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page