User Guide

Everything you need to install, configure, and build with Montycat — from getting started to governance, advanced operations, and security.

To start Montycat run: montycat hunt.
Command will start the Montycat process.
Database engine will be initialized and available on 0.0.0.0:21210.
Subscription server is listening to 0.0.0.0:21211.

To stop Montycat run: montycat home.
Command will stop the Montycat process gracefully including all running background tasks.

To get help run: montycat help.

montycat hunt 
montycat home 
montycat help

Before proceeding with queries you have to setup superowner credentials.
Otherwise the engine will decline requests.
Typically returns true or false with error description.

montycat create-superowner username <username> password <password>

First install the Montycat client library

#for Node based JavaScript/TypeScript clients   
npm install montycat --save   
   
#for Python clients   
pip install montycat   
   
#for Dart/Flutter clients   
dart pub add montycat   
   
#for Rust clients   
cargo add montycat

To setup connection instantiate a class Engine.
The store is an optional property so you can define it later but make sure you define it prior to calling to database.

# connection.py
from montycat import Engine

connection = Engine(
    host="<host>",
    port=<port>,
    username="<username>",
    password="<password>",
    store="<store name>"
)

Alternatively, you can instantiate a class using ready-to-go method and pass a connection string as an argument.

# connection.py
from montycat import Engine

connection = Engine.from_uri("montycat://<username>:<password>@<host>:<port>/<store name>")

In Montycat, a Keyspace is like a namespace or logical database where your data lives.
It defines how and where data is stored — either persistent on disk or in memory.
Under the hood, each Keyspace is automatically divided into smaller logical units called Volumes, depending on your system parameters such as RAM, disk space, and CPU cores.
Volumes are self-managing and require no manual intervention.

Persistent storage is based on an LSM tree and provides WAL (Write-Ahead Logging), durability, atomicity, and consistency.
Use this when you want data to survive restarts and remain available long-term.

In-Memory storage Handles data that lives entirely in memory by default (with optional snapshots for persistence).
It based on lock-free thread-safe hash table with time complexity O(1) so multiple clients can have concurrent read/write access. It is extremely fast - capable of processing over 1,000,000 structured read/write operations per second.

# keyspaces.py
from montycat import Keyspace
from connection import connection #connection.py

class Employees(Keyspace.Persistent):
    keyspace = "Employees"

class EmployeesInMem(Keyspace.InMemory):
    keyspace = "EmployeesInMem"

Employees.connect_engine(connection)
EmployeesInMem.connect_engine(connection)

When defining a persistent keyspace, you can specify additional optional parameters such as cache and compression.
Those options allow you to fine-tune storage performance and resource usage for persistent workloads.

Must be an integer value representing the cache size in megabytes (MB).
The minimum allowed value is 10 MB.
If not provided, the default value is 10 MB.

A boolean flag (true or false).
Defaults to false if not specified.

from montycat import Keyspace

class Employees(Keyspace.Persistent):
    keyspace = "Employees"
    cache = 100
    compression = True

Montycat is a NoSQL database with a flexible data mesh architecture.
It offers a high degree of flexibility: you can work with schema definitions anywhere along the spectrum between a simple key-value store and a strict SQL-like engine.

  • Schemaless - no schemas.
  • Schema-like (Hybrid) - schema defined on the client side.
  • Schemaful - schema defined on the client side and enforced on database level with relation to the particular keyspace.

A schema is optional (so you can save any values such as string, integers, floats, complex types) but highly recommended to keep data well-structured and organized — especially in microservice architectures where multiple services interact with Montycat.
Each Keyspace in Montycat supports multiple schemas (both enforced and non-enforced) and keeps track of them transparently.
The base Schema class defines a structured schema for records stored inside a Montycat Keyspace.
A schema describes the shape of the data: what fields exist, their types, and their intended usage.
This ensures records remain consistent, strongly typed, and self-describing inside the database.

Note for Rust Client: schema definitions need serde with its derive feature alongside montycat, because structs derive Serialize and Deserialize: cargo add serde --features derive. The RuntimeSchema derive itself comes from montycat — no extra crate is required.

from montycat import Schema

class EmployeesSchema(Schema):
    username: string
    location: string
    age: number

Montycat clients support runtime-safe migrations, eliminating the need to execute migrations from a separate file.
Migrations can be executed automatically on each application startup.
If a migration has already been applied, the operation will not be repeated.
Instead, the client will typically return a response with key-value pair {status: false}:

# migrations.py
from keyspaces import Employees, EmployeesInMem #keyspaces.py

async def migrate():

    res1 = await Employees.create_keyspace()
    res2 = await EmployeesInMem.create_keyspace()

    return res1, res2

    # {"status": True, "payload": None, "error": None}

Alternatively you can use CLI command to create a keyspace.
You will be prompted to provide your credentials. Expected return true or false (with error descripton).

CLI typically requires superowner privileges.

montycat console
> create-keyspace store <store name> keyspace <keyspace name>

exit

If you put persistent in the end, the keyspace you are going to create will be persistent.
Cache is an optional argument. Has to be more than 10 MB, if set to a value less than 10 MB will fall back to 10 MB.
The last argument is also optional. It represents compression and if present will set compression to true.

montycat console
> create-keyspace store <store name> keyspace <keyspace name> persistent cache 100 compression

exit

Client's calls to Montycat are asynchronous, so make sure you await the completion.
Montycat always returns structured, already deserialized and parsed results.
All methods are identical across keyspaces, regardless of their type.

Note: If you are using schema and ORM, you must serialize the data before running a query by calling the built-in method .serialize().

Rust Client does not require explicit serialization and handles schemas natively with derive macro #[derive(RuntimeSchema)].

Note for Rust Client: Struct MontycatResponse introduced to represent responses from the database engine. Struct has a method .parse_response() to handle the parsing and deserialization of responses. MontycatResponse can represent responses with different payload types by using Rust's generics feature, serde_json::Value or exact types. Can be used as following:
MontycatResponse::<Option<Value>>::parse_response(res)
where res is the raw response bytes from the database and <Option<Value>> is the expected payload type.

.parse_response() method returns Result<T, E> type so you may expect to handle potential errors during parsing.

This approach allows handling various response structures flexibly and type-safely.

# main.py
from schemas import EmployeesSchema #schemas.py
from keyspaces import Employees #keyspaces.py

async def query():

    record = EmployeesSchema(
        username="Name",
        location="Location",
        age=21
    ).serialize()

    res1 = await Employees.insert_value(record)

    # {"status": True, "payload": "128222336824100726154851618391811195396", "error": None}

    res2 = await Employees.get_value(key=res1['payload'])

    # {"status": True, "payload": {"username": "Name", "location": "Location", "age": 21}, "error": None}

Beyond exact lookups, Montycat can rank items by meaning using built-in vector search — the retrieval layer for RAG and AI-agent memory. On the Montycat Semantic edition it is enabled by default, so you can search a keyspace right away, no setup required.
Full details, models, and the enable/disable switch are in the Semantic Search section.

from keyspaces import Employees #keyspaces.py

# Rank stored items by MEANING, not keywords. Two variants:
#   get_values -> each hit is {__key__, __score__, __value__}
#   get_keys   -> each hit is {__key__, __score__} (lighter; fetch a value later with get_bulk)

res = await Employees.semantic_search_get_values("engineers based near the coast", limit=5)

# {
#   "status": True,
#   "payload": [
#     {"__key__": "128222336824100726154851618391811195396", "__score__": 0.82,
#      "__value__": {"username": "Name", "location": "Location", "age": 21}}
#   ],
#   "error": None
# }

# Keys + scores only, with a cosine-similarity floor (range [-1, 1]):

keys = await Employees.semantic_search_get_keys("engineers based near the coast", limit=5, min_score=0.35)

# {
#   "status": True,
#   "payload": [{"__key__": "128222336824100726154851618391811195396", "__score__": 0.82}],
#   "error": None
# }