Skip to main content

dhara

Code style: crackerjack Runtime: oneiric uv Python: 3.13+

dhara is a modern continuation of Durus, a persistent object system for applications written in the Python programming language. It could be called a noSQL database. However, it does provide "ACID" properties (Atomicity, Consistency, Isolation, Durability).

The implementation of dhara is not multi-threaded but does provide concurrency via a client/server model. It is optimized for read heavy work loads and aggressively caches persistent objects in memory. For many applications, this design enables good performance with minimal effort from application programmers.

Bodai Ecosystem Role

Dhara is the curator of the Bodai ecosystem — the persistent object storage backend for adapter configs, service lifecycle state, and ecosystem events consumed by Mahavishnu, Akosha, Session-Buddy, Crackerjack, and Oneiric.

Standalone, Dhara is a modern continuation of Durus — a Python persistent object system with ACID properties. See bodai/docs for how Dhara fits into the broader ecosystem.

Origin

dhara was originally written by the MEMS Exchange software development team at the Corporation for National Research Initiatives (CNRI). dhara was designed to be the storage component for the Python-powered web sites operated by the MEMS Exchange. See doc/README_CNRI.txt for more details.

Overview

dhara offers an easy way to use and maintain a consistent collection of object instances used by one or more processes. Access and change of a persistent instances is managed through a cached Connection instance which includes commit() and abort() methods so that changes are transactional.

CLI Commands

Dhara provides a unified CLI with three command groups:

MCP Server Commands (for AI/Agent Workflows)

dhara mcp start              # Start MCP server
dhara mcp stop               # Stop MCP server
dhara mcp status             # Check server status
dhara mcp health             # Health check

Database Commands (Dhara Storage Operations)

dhara db start               # Start Dhara storage server
dhara db client              # Connect to server (interactive)
dhara db pack                # Reclaim storage space

Common options for database commands:

  • --file PATH or -f PATH - Database file path
  • --host HOST or -h HOST - Server host (default: 127.0.0.1)
  • --port PORT or -p PORT - Server port (default: 2972)
  • --readonly - Open in read-only mode

Dhara-Specific Commands

dhara adapters               # List registered adapters
dhara storage                # Display storage information
dhara admin                  # Launch admin shell (IPython)

Validation

The preferred local validation path is crackerjack:

python -m crackerjack qa-health
python -m crackerjack run-tests

Use direct pytest commands when you need to isolate a single file or debug a specific failure.

Configuration Surfaces

Dhara currently exposes two configuration layers:

  • dhara.core.config.DharaSettings is the canonical runtime settings model for the CLI and MCP server
  • dhara.config remains available for lightweight dataclass helpers and compatibility with older code

For service startup, operator configuration, and environment-variable overrides, use DharaSettings.

Deprecation Policy

Dhara is in an active compatibility-reduction window.

  • Deprecated compatibility imports remain available in 0.8.x
  • They are planned for stronger enforcement in 0.9.x
  • Convenience compatibility shims are candidates for removal in 1.0.0

The current policy and migration targets are documented in docs/LEGACY_COMPATIBILITY_AND_REMOVAL_PLAN.md.

Quick Demo

Start a Dhara server:

dhara db start

This starts a Dhara storage server using a temporary file and listening for clients on localhost port 2972.

Connect as a client:

dhara db client

This opens an interactive IPython shell connected to the storage server. You have access to a dictionary-like persistent object, root. If you make changes to items of root and run connection.commit(), the changes are written to the file. If you make changes and then run connection.abort(), the attributes revert back to the values they had at the last commit.

Multiple clients: Run dhara db client in another terminal to see how committed changes to root in one client are available in other clients when they synchronize via connection.abort() or connection.commit().

Stop the server: Press Control-C in the server terminal.

Persistence example:

# Start server with a persistent file
dhara db start --file test.dhara

# Connect, make changes, commit
dhara db client --file test.dhara
# In the shell:
# >>> root["hello"] = "world"
# >>> connection.commit()

# Stop and restart - data persists
dhara db start --file test.dhara
dhara db client --file test.dhara
# >>> root["hello"]
# 'world'

Direct file access (no server):

dhara db client --file test.dhara

All commands accept --help for more options.

Using dhara in a Program

To use dhara, a Python program needs to make a Storage instance and a Connection instance. For the Storage instance, you have two choices: AsyncFileStorage or ClientStorage. If your program is to be one of several processes accessing a shared collection of objects, then you want ClientStorage. If your program has no competition, then choose AsyncFileStorage. There is only one Connection class, and the constructor takes a storage instance as an argument.

Example using AsyncFileStorage to open an async Connection to a file:

import asyncio
from dhara.core.connection import AsyncConnection
from dhara.storage.async_file import AsyncFileStorage

async def main() -> None:
    storage = AsyncFileStorage("test.dhara")
    await storage.init()
    connection = await AsyncConnection.new(storage)

asyncio.run(main())

Example using ClientStorage to open a Connection to a dhara server:

from dhara.core.connection import Connection
from dhara.storage.client import ClientStorage

connection = Connection(ClientStorage())

Note that the ClientStorage constructor supports the address keyword that you can use to specify the address to use. The value must be either a (host, port) tuple or a string giving a path to use for a unix domain socket. If you provide the address you should be sure to start the storage server the same way. The dhara command line tool also supports options to specify the address.

The connection instance has a get_root() method that you can use to obtain the root object.

In your program, you can make changes to the root object attributes, and call connection.commit() or connection.abort() to lock in or revert changes made since the last commit. The root object is actually an instance of dhara.collections.dict.PersistentDict, which means that it can be used like a regular dict, except that changes will be managed by the Connection. There is a similar class, dhara.collections.list.PersistentList that provides list-like behavior, except managed by the Connection.

PersistentList and PersistentDict both inherit from dhara.core.persistent.Persistent, and this is the key to making your own classes participate in the dhara persistence system. Just add Persistent class A's list of bases, and your instances will know how to manage changes to their attributes through a Connection. To actually store an instance x of A in the storage, though, you need to commit a reference to x in some object that is already stored in the database. The root object is always there, for example, so you can do something like this:

# Assume mymodule defines A as a subclass of Persistent.
from mymodule import A
x = A()
root = connection.get_root() # connection set as shown above.
root["sample"] = x           # root is dict-like
connection.commit()          # Now x is stored.

Subsequent changes to x, or to new A instances put on attributes of X, and so on, will all be managed by the Connection just as for the root object. This management of the Persistent instance continues as long as the instance is in the storage. Sometimes, though, we wish to remove "garbage" Persistent instances from the storage so that the file can be smaller. This garbage collection can be done manually by calling the Connection's pack() method. If you are using a storage server to share a Storage, you can use the gcinterval argument to tell it to take care of garbage collection automatically.

Non-Persistent Containers

When you change an attribute of a Persistent instance, the fact that the instance has been changed is noted with the Connection, so that the Connection knows what instances need to be stored on the next commit(). The same change-tracking occurs automatically when you make dict-like changes to PersistentDict instances or list-like changes to PersistentList instances. If, however, you make changes to a non-persistent container, even if it is the value of an attribute of a Persistent instance, the changes are not automatically noted with the Connection. To make sure that your changes do get saved, you must call the _p_note_change() method of the Persistent instance that refers to the changed non-persistent container. You can see an example of this by looking at the source code of PersistentDict and PersistentList, both of which maintain a non-persistent container on a data attribute, shadow the methods of the underlying container, and add calls to self._p_note_change() in every method that makes changes.

Storage back-ends

This version of dhara includes a number of back-end storage implementations that may be used. The default is AsyncFileStorage, a thin wrapper over AsyncSqliteStorage that maps a filesystem path to a sqlite+aiosqlite:// URL. It accepts the path-style constructor that callers expect from the legacy FileStorage API while delegating to the canonical async SQLite backend.

Note: SHELF-1 is removed in 0.11.0. New and migrated databases should use AsyncFileStorage (path-style) or AsyncSqliteStorage (URL-style) directly.

Finally, there is an experimental Sqlite storage module, SqliteStorage. The module uses a SQLite3 database to persist object data. One disadvantage of this module compared to the others is that online backups are more difficult (for the other two it is safe to just copy the file while the server is running). You also lose the ability to do point-in-time recovery (which the other two storage implementations provide, assuming you did not yet pack the DB).

Acknowledgements

dhara is a modern fork and continuation of Durus, originally developed by the MEMS Exchange software development team at the Corporation for National Research Initiatives (CNRI). We are grateful for the foundational work done by the original Durus developers.

This modern version (dhara) includes:

  • Modern Python 3.13+ type hints
  • Enhanced serialization options (msgspec)
  • Oneiric configuration and logging integration
  • MCP server for modern AI/agent workflows
  • Comprehensive security and performance improvements

The name dhara (ध्रुव) is Sanskrit for "immovable, eternal, constant," or "Pole Star" - complementing the original Latin name Durus, meaning "hard, sturdy, tough, enduring."

License

dhara is released under an open-source license. See LICENSE.txt for details.

Release files for dhara 0.17.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dhara 0.17.0
File Size Uploaded
dhara-0.17.0.tar.gz 51.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dhara 0.17.0
File Interpreter ABI Platform
dhara-0.17.0-py3-none-any.whl Python 3 none any Details

Total release size: 51.3 MB

Release files / dhara-0.17.0.tar.gz

Download URL dhara-0.17.0.tar.gz
Size 51.0 MB
Tags Source
SHA-256 checksum
How to use checksums
874bbcb3ce12ad4f3b27bfd8e9d905a3faa9b7234929cfe88ab9d149a514e171
BLAKE2b-256 checksum
How to use checksums
82bf5aab63d823c25781aeae42f318b8d446a5ceda2ba30a10640fd042305192
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / dhara-0.17.0-py3-none-any.whl

Download URL dhara-0.17.0-py3-none-any.whl
Size 282.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e20dd0258bd258e0ac9aba1b178610cc925ad8c0947a895faf13dc97ea23e194
BLAKE2b-256 checksum
How to use checksums
84c21f7b86eeee754a7cf23a116428890622f717323a63496a7b83ba523cbef1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.21.0

2 release files

0.20.2

2 release files

0.20.1

2 release files

0.20.0

2 release files

0.19.2

2 release files

0.19.1

2 release files

0.19.0

2 release files

0.18.0

2 release files

0.17.2

2 release files

0.17.1

2 release files

This release

0.17.0 This release

2 release files

0.16.0

2 release files

0.15.2

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.13.2

2 release files

0.13.1

2 release files

0.13.0

2 release files

0.12.1

2 release files

0.11.2

2 release files

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release 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