User Guide
Everything you need to install, configure, and build with Montycat — from getting started to governance, advanced operations, and security.
How to get started?
CLI
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 helpBefore 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>Setup connection
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 montycatTo 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>"
)From uri
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>")Define a keyspace
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 Keyspace
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 Keyspace
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)Cache and compression
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.
Cache
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.
Compression
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 = TrueDefine a schema
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.
Schema Modes in Montycat
- 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: numberSetup runtime migrations
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}Create a Keyspace using CLI
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>
exitIf 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
exitRun first query
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}Semantic search
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
# }