Release Notes
Updates, fixes, and improvements across the Montycat database and its client libraries.
61 releasesLatest update · August 26, 2026
August 26, 2026
- Published Meow 0.1.2 as a Universal macOS DMG and an amd64 Linux DEB package
- Published Meow 0.1.0 as a Windows x64 EXE installer
- Meow detects missing or outdated Montycat engines and offers the appropriate package
- Meow checks for new versions of itself; downloads begin only after user approval and open in the operating-system installer
August 22, 2026
- The create-superowner command is now accepted only from clients connected through an IPv4 or IPv6 loopback address
- Remote create-superowner requests are rejected with an explicit failure, preventing initial superowner credentials from being established over a network-facing connection
August 15, 2026
- Read commands accept order: "ascending" or order: "descending" for key-range, bulk, and lookup results
- Ordered in-memory reads are now deterministic, and pagination consistently uses the half-open [start, stop) range
- Semantic search remains ranked by similarity and rejects key ordering, preserving relevance order
Rust Client v0.4.1
LatestAugust 15, 2026
- Added the exported ResultOrder enum with Ascending and Descending variants
- get_keys, get_bulk, lookup_keys_where, and lookup_values_where accept an optional ResultOrder for deterministic forward or reverse reads
- Ordering composes with half-open [start, stop) limits, allowing stable forward and reverse pagination
Dart Client v1.2.2
LatestAugust 15, 2026
- Added the exported ResultOrder enum with ascending and descending values
- getKeys, getBulk, lookupKeysWhere, and lookupValuesWhere accept an optional order argument for deterministic forward or reverse reads
- Ordering composes with half-open [start, stop) limits, allowing stable forward and reverse pagination
Node Client v1.3.2
LatestAugust 15, 2026
- Added the exported ResultOrder type: "ascending" | "descending"
- getKeys, getBulk, lookupKeysWhere, and lookupValuesWhere accept an optional order property for deterministic forward or reverse reads
- Ordering composes with half-open [start, stop) limits, allowing stable forward and reverse pagination
Python Client v1.2.2
LatestAugust 15, 2026
- Added the exported ResultOrder enum with ASCENDING and DESCENDING values
- get_keys, get_bulk, lookup_keys_where, and lookup_values_where accept an optional order argument for deterministic forward or reverse reads
- Ordering composes with half-open [start, stop) limits, allowing stable forward and reverse pagination
Dart Client v1.2.1
LatestAugust 12, 2026
- Applied Dart analyzer and formatter cleanups across public exports, the engine, keyspaces, connection pooling, transport code, and tests
- No application changes are required: this release does not alter the public API or runtime behavior
Node Client v1.3.1
LatestAugust 12, 2026
- Large responses now retain incoming socket chunks and join them only once when a complete newline-framed response is available
- The new reader avoids repeatedly copying the entire response as it grows, improving memory and CPU efficiency for very large payloads
- The optimization applies to direct requests, pooled connections, and subscriptions, and requires no application code changes
Python Client v1.2.1
LatestAugust 12, 2026
- Response frames are no longer constrained by asyncio’s 64 KiB stream-reader limit or by a replacement fixed ceiling. Large policy-history and bulk-read responses are accumulated safely while preserving frame boundaries and any bytes belonging to the next response
- The framing-safe reader works consistently across direct, pooled, and subscription connections, preserving the client’s previous ability to receive very large responses
- Custom keys are encoded as UTF-8 before hashing. Their hashes remain unchanged while supporting xxhash releases that require bytes instead of accepting Python strings
Montycat v1.3.0
LatestAugust 3, 2026
- New init-self-tls command generates a self-signed certificate and private key inside the engine, installs them at the configured TLS paths, and persists those paths — no certificate authority, no openssl binary, and no mounted certificate required. It returns the paths, the identities covered, the validity period, and the certificate’s SHA-256 fingerprint
- Setting MONTYCAT_INIT_SELF_TLS=true generates the certificate at startup before any listener binds, so a container comes up with TLS already active on the same run. Optional MONTYCAT_TLS_DNS_NAMES, MONTYCAT_TLS_IP_ADDRESSES, MONTYCAT_TLS_VALIDITY_DAYS, and MONTYCAT_TLS_FORCE tune it. Existing certificate material is never replaced unless force is given explicitly, so restarting a container does not churn the certificate
- Certificates cover localhost, 127.0.0.1, and ::1 for 365 days by default. Bind addresses that mean “every interface” — 0.0.0.0 and :: — are deliberately excluded, because they are not addresses a client dials and a certificate naming them authenticates nothing. Deployments reached by IP should pass the external address they are actually dialed on
- Sent to a server that is already running, init-self-tls reports restart_required: the listeners keep the certificate they were built with until restart. The environment variable is the only route that activates TLS on the startup that runs it
- A missing or unusable certificate still never prevents the server from starting, but it can no longer do so quietly. Having no certificate configured is now reported separately from having one that is configured but broken; the second names both paths and states that the listener is continuing in plaintext and connections to it are not encrypted. Each listener also reports the mode it came up in. Private key contents are never written to the log
- A private key that does not belong to its certificate is now rejected at startup. Such a pair previously loaded without complaint, the listener advertised TLS, and then every connection failed at the handshake
- Certificate and key are installed atomically and only after the generated pair has been validated by building a real TLS configuration from it, so a failed generation can no longer destroy a working certificate. The private key is written owner-read-only
- The bundled CLI exposes init-self-tls in the interactive console, adds disable-tls to clear the configured certificate and key paths — a local operation needing no server, credentials, or network — and takes passwords by prompt or --password-stdin so a secret never has to appear in the process list
- Client certificate verification is uneven, so know which one you are using. The Node, Python, and Dart clients accept any certificate: traffic is encrypted but the server is not authenticated. The Rust client 0.4.0 can verify a Montycat-generated self-signed certificate when given its PEM through Engine::with_tls_ca_file, which trusts it alongside the public certificate authorities and keeps chain and hostname checking on. The SHA-256 fingerprint is returned so pinning can be added to the remaining clients without a protocol change
- New semantic_upsert_vectors command attaches caller-supplied vectors to items that already exist in a keyspace, and insert and insert_bulk accept a vector inline with the value. Vectors are validated — dimension against the enrolled model, finite components, normalization — before anything reaches the resident graph or the durable vector tree, so a bad entry in a batch cannot leave the two out of step
- Semantic search accepts a query vector, and when one is present the query text is ignored and no embedding model is invoked at all. Together with supplied vectors this makes the vector index measurable independently of model inference, and lets embeddings produced by another model or pipeline be stored and searched directly
- Background embedding can no longer overwrite a client-supplied vector. Keys carrying a precomputed vector are marked, and queued or backfilled text embedding skips them; a later ordinary value write clears the marker and re-enqueues text embedding, which is the point at which re-embedding is correct
- Fixed persistent index keys that could collide across JSON types. The old key concatenated the field hash with the serialized value and the record key without marking the boundary between them, so values of different types could produce identical bytes — the string "2" and the number 2 among them — and a lookup could return rows that did not match the criterion. Keys are now versioned, type-tagged, and length-framed. A migration rebuilds the index when the durable format marker is missing or old, committing the marker last so an interrupted run simply repeats on the next start
- Fixed a subscription whose client had disconnected staying open until the next event on that keyspace, holding the watchers that remove-keyspace and remove-store wait on. Subscription connections are now also closed explicitly rather than dropped, so a TLS client is not left waiting out its own shutdown timeout — visible before as a 30-second stall on every subscription teardown
- Interactive search no longer queues behind background embedding: query embedding runs on a dedicated engine with its own thread budget, so a long document batch cannot hold the model ahead of a search. HNSW parameters are now explicit named defaults rather than whatever the linked library happened to use, and a denser profile was measured and rejected for costing more throughput than the recall it bought
- Index size limits are now explicit and explained. Exceeding one names the backend, the actual size, and the maximum, and the limits are byte constants — 4 KiB in-memory, 64 KiB persistent — rather than a character count, which is not the unit a byte-keyed index actually cares about
Rust Client v0.4.0
LatestAugust 3, 2026
- Added opt-in connection pooling. Reusing a connection measured 2.56x faster on loopback against a debug engine — 161 µs down to 63 µs per list-owners — and the gap widens over a network, where the handshake costs a full round trip before the query is sent, and widens again with TLS. Enable it with Engine::from_uri(..)?.with_pool(PoolConfig::default()) and no call site changes; PoolConfig and ConnectionPool are exported from the crate root
- Reuse one Engine for the process lifetime: cloning is cheap and shares the pool, while building a fresh Engine per request builds a fresh empty pool and amortises nothing. Pooling is per-Engine rather than global. Call Engine::close_pool() before exit — Drop cannot be async, so without it TLS connections close without close_notify and the server logs an error for each one
- Added Engine::with_tls_ca_file, which trusts a PEM certificate for that engine’s TLS connections and enables TLS. It adds the certificate alongside the public WebPKI roots, so a private CA or a Montycat-generated self-signed certificate can be verified while chain and hostname checking stay on. The dialed name or IP must appear in the certificate’s Subject Alternative Names
- Added precomputed vectors, for embeddings produced by another model, a batch pipeline, or an existing vector store, on insert_value, insert_custom_key_value, update_value, insert_bulk, update_bulk, and all four semantic_search methods. Requires a Montycat Semantic server 1.3.0 or newer
- Breaking: those methods gained a vector parameter, and Rust has no default arguments, so every call site needs one more argument. Passing None keeps the previous behavior and the compiler lists every site that needs it. Engine also gained private fields, so struct-literal construction no longer compiles — use Engine::new or Engine::from_uri
- MontycatClientError now implements Display and std::error::Error, so it composes with the question-mark operator, Box<dyn Error>, anyhow, and thiserror. The usual shape of an axum or tokio main previously failed to compile and required mapping the error at every call site
- Fixed a subscription frame that could be delivered concatenated with the next one or dropped entirely, and a response read that rescanned its whole buffer on every chunk, making a large response quadratic in the number of chunks
August 3, 2026
- Added opt-in connection pooling. Every request previously opened a socket, sent one request, read one response, and closed it; reusing the connection removes the handshake from every call after the first. Pass pool: const PoolConfig() to Engine and no call site changes, then await closeAllPools() before exit. PoolConfig and closeAllPools are exported from package:montycat/montycat.dart
- Pools live in a library-level registry keyed by host, port, and useTls rather than on the Engine. That matters more here than in the other clients: keyspace state is per-instance, so a Flutter app building a keyspace per screen or per rebuild would otherwise create a pool per instance. The key is read at request time, so flipping useTls after connectEngine cannot reuse a plaintext connection for a TLS engine
- On mobile, prefer a short idleTimeout. iOS and Android close sockets when an app is backgrounded, so every pooled connection is dead on resume; the client detects that before reusing one, but the cost is a user-visible round trip. Apps that observe connectivity should call closeAllPools() when the network changes, since Wi-Fi to cellular invalidates every pooled connection
- Added precomputed vectors, for embeddings produced by another model, a batch pipeline, or an existing vector store. insertValue, insertCustomKeyValue, and updateValue take an optional vector; insertBulk takes vectors paired with bulkValues by position; updateBulk takes vectors for numeric keys and customVectors for custom keys. Requires a Montycat Semantic server 1.3.0 or newer
- All four semanticSearch methods take an optional query vector, which bypasses text embedding entirely — the query string may be empty when one is supplied
- Dimensions are validated against the keyspace’s enrolled model before anything reaches the index, so a bad entry in a batch cannot leave the vector graph and the durable store disagreeing. A vector you supplied is never overwritten by background embedding; a later ordinary write to that item clears the protection and re-embeds from its text
August 3, 2026
- Added opt-in connection pooling. Every request previously opened a TCP connection, sent one request, read one response, and closed it; reusing the connection removes the handshake from every call after the first. Pass pool: {} to the Engine constructor and no call site changes, then call closeAllPools() before exit or idle sockets keep the process alive. closeAllPools and the PoolConfig type are exported from the package root
- Pools live in a module-level registry keyed by host, port, and useTls, so every keyspace class pointing at one server shares a single pool rather than each opening its own. Disabled by default and deliberately conservative when enabled — maxIdle 8, idleTimeoutMs 30000 — because an idle pooled connection still holds one of the engine’s connection permits. Subscriptions are never pooled, and a connection is held exclusively for one request and its response, so concurrent calls each get their own
- Added precomputed vectors, for embeddings produced by another model, a batch pipeline, or an existing vector store. insertValue, insertCustomKeyValue, and updateValue take an optional vector; insertBulk takes vectors paired with bulk by position; updateBulk takes vectors for numeric keys and customVectors for custom keys. Requires a Montycat Semantic server 1.3.0 or newer
- All four semanticSearch methods take an optional query vector, which bypasses text embedding entirely — the query string may be empty when one is supplied
- Dimensions are validated against the keyspace’s enrolled model before anything reaches the index, so a bad entry in a batch cannot leave the vector graph and the durable store disagreeing. A vector you supplied is never overwritten by background embedding; a later ordinary write to that item clears the protection and re-embeds from its text
- connectEngine copies the pool configuration explicitly instead of propagating a reference through Object.assign, and its parameter is no longer typed any
August 3, 2026
- Added opt-in connection pooling. Every request previously opened a TCP connection, sent one request, read one response, and closed it; reusing the connection removes the handshake from every call after the first. Pass pool=PoolConfig() to Engine and nothing else changes, then call close_all_pools() before exit. PoolConfig and close_all_pools are exported from the package root
- Pools live in a module-level registry keyed by host, port, and TLS, so every keyspace class pointing at one server shares a single pool. Disabled by default and deliberately conservative when enabled — max_idle 8, idle_timeout 30 seconds — because an idle pooled connection still holds one of the engine’s connection permits. Subscriptions are never pooled, and a connection is held exclusively for one request and its response, so concurrent coroutines each get their own
- Added precomputed vectors, for embeddings produced by another model, a batch pipeline, or an existing vector store. insert_value, insert_custom_key_value, and update_value take an optional vector; insert_bulk takes vectors paired with bulk_values by position; update_bulk takes vectors for numeric keys and custom_vectors for custom keys. Requires a Montycat Semantic server 1.3.0 or newer
- All four semantic_search_ methods take an optional query vector, which bypasses text embedding entirely — the query string may be empty when one is supplied
- Dimensions are validated against the keyspace’s enrolled model before anything reaches the index, so a bad entry in a batch cannot leave the vector graph and the durable store disagreeing. A vector you supplied is never overwritten by background embedding; a later ordinary write to that item clears the protection and re-embeds from its text
- Fixed a subscription callback that could fire once per socket chunk rather than once per frame, delivering two events as one or dropping a partial frame
July 31, 2026
- New get-semantic-status command reports the semantic configuration the engine actually holds — the database-wide switch, the default model and field, and per keyspace its model, dimensions, field, storage type, and whether a backfill is still pending. A superowner may read any scope; any other owner needs the manage-semantic capability on the requested store and keyspace, so an unscoped database-wide readback is superowner-only
- New reembed-semantic-search command changes the embedding model or field of an already-enrolled keyspace, which enable-semantic-search deliberately refuses to do. It clears that keyspace’s vectors, writes the replacement configuration, and starts a full backfill in one operation, reporting the previous model alongside the new one. It is keyspace-scoped by design and is authorized by manage-semantic, with the requested model checked against the owner’s permitted models
- Previously the only way to change an enrolled keyspace’s model was to disable semantic search with drop_vectors and enable it again, which left the keyspace unsearchable in between. Re-embedding removes that gap
- list_all_depending_keys now returns an empty map instead of null when an in-memory key has no dependents, matching what persistent keyspaces already returned. One condition previously arrived as two different types, so every client had to accept both; a key that does have dependents is unaffected
- enable-semantic-search now explains itself when it declines to change an enrolled keyspace, naming the current model and dimensions and pointing at reembed-semantic-search, instead of returning a bare refusal
- The bundled CLI exposes the new commands in the interactive console, and its top-level enable-semantic-search and disable-semantic-search now accept --store and --keyspace to target a single store or keyspace instead of only the whole database
- montycat help now separates top-level commands from console-only commands and lists each one’s required arguments, rather than presenting a single flat list
July 31, 2026
- Added get_semantic_status, which reports the semantic configuration the server actually holds rather than what the caller assumed: the database-wide switch, the default model and field, and each keyspace’s model, dimensions, field, storage type, and whether a backfill is still pending
- Added reembed_semantic_search, the supported way to change an enrolled keyspace’s embedding model. It drops that keyspace’s vectors, records the new configuration, and starts a complete backfill in one operation, reporting the previous model alongside the new one
- Corrected the documentation for enable_semantic_search, which never modified an already-enrolled keyspace despite reading as though it could switch models, and for disable_semantic_search, whose drop_vectors option was wrongly described as required before changing models. Behavior is unchanged in both cases
July 31, 2026
- Added getSemanticStatus, which reports the semantic configuration the server actually holds rather than what the caller assumed: the database-wide switch, the default model and field, and each keyspace’s model, dimensions, field, storage type, and whether a backfill is still pending
- Added reembedSemanticSearch, the supported way to change an enrolled keyspace’s embedding model. It drops that keyspace’s vectors, records the new configuration, and starts a complete backfill in one operation, reporting the previous model alongside the new one
- Both methods return typed results — SemanticStatus, SemanticKeyspaceStatus, and SemanticReembedResult, all exported from package:montycat/montycat.dart — instead of an untyped map, and throw StateError when the server reports failure. SemanticStatus.keyspace(store, keyspace) looks up a single entry without composing the "store/keyspace" key by hand
- Corrected the documentation for enableSemanticSearch, which never modified an already-enrolled keyspace despite reading as though it could switch models, and for disableSemanticSearch, whose dropVectors option was wrongly described as required before changing models. Behavior is unchanged in both cases
July 31, 2026
- Added getSemanticStatus, which reports the semantic configuration the server actually holds rather than what the caller assumed: the database-wide switch, the default model and field, and each keyspace’s model, dimensions, field, storage type, and whether a backfill is still pending
- Added reembedSemanticSearch, the supported way to change an enrolled keyspace’s embedding model. It drops that keyspace’s vectors, records the new configuration, and starts a complete backfill in one operation, reporting the previous model alongside the new one
- Corrected the documentation for enableSemanticSearch, which never modified an already-enrolled keyspace despite reading as though it could switch models, and for disableSemanticSearch, whose dropVectors option was wrongly described as required before changing models. Behavior is unchanged in both cases
July 31, 2026
- Added get_semantic_status, which reports the semantic configuration the server actually holds rather than what the caller assumed: the database-wide switch, the default model and field, and each keyspace’s model, dimensions, field, storage type, and whether a backfill is still pending
- Added reembed_semantic_search, the supported way to change an enrolled keyspace’s embedding model. It drops that keyspace’s vectors, records the new configuration, and starts a complete backfill in one operation, reporting the previous model alongside the new one
- Added do_snapshots_for_keyspace. The method had always been spelled do_snaphots_for_keyspace, missing an "s" — this was the only client of the four to misspell it. The old name still works as a deprecated alias and will be removed in a future major release
- Corrected the documentation for enable_semantic_search, which never modified an already-enrolled keyspace despite reading as though it could switch models, and for disable_semantic_search, whose drop_vectors option was wrongly described as required before changing models. Behavior is unchanged in both cases
July 29, 2026
- Fixed: a request whose payload contained the word "subscribe" never returned. Subscription mode was detected by scanning the serialized request, so an ordinary insert entered the streaming branch, which has no read timeout. A request is now a subscription only when the caller supplies a callback
- Documented real-time subscriptions, TLS via the tls feature, owner and access management, and response parsing through MontycatResponse
- Fixed the dependency snippet, which pinned a version predating the governance APIs documented alongside it, and a semantic-search example that omitted the SemanticModel import
- Added tests covering u128 key preservation through response parsing, including double-encoded payloads, and type mismatches returning a parsing error rather than panicking
- The published crate no longer contains repository-only files such as workflow definitions and internal notes
July 29, 2026
- Fixed: a request whose stored value contained the word "subscribe" returned a SubscriptionHandle instead of the response envelope, and its socket was never closed. Subscription mode was inferred from the serialized request body; it is now determined solely by whether the caller supplies a callback
- Documented real-time subscriptions, TLS connections, owner and access management, and the response envelope, none of which had examples before
- Fixed the installation snippet, which pinned a version predating the governance APIs documented alongside it
- The published package no longer contains stray .DS_Store files, which had been included in previous releases
- Added continuous integration across Linux, macOS, and Windows on the stable and beta SDKs; publishing now runs analysis and the test suite first
- Added transport-level tests covering request framing, response parsing, u128 key preservation, and subscription teardown
July 29, 2026
- Documented real-time subscriptions, TLS connections, owner and access management, and the response envelope, none of which had examples before
- Added continuous integration on Node 18, 20, 22, and 24 across Linux and macOS; the test suite previously never ran in CI
- Publishing to npm now runs the test suite first
- This client was unaffected by the subscription-detection defect fixed in the Python, Dart, and Rust releases of the same date: it has always identified subscriptions from an explicit request field rather than by scanning the payload
July 29, 2026
- Fixed: a request whose stored value contained the word "subscribe" never returned. Subscription mode was inferred from the serialized request body, so an ordinary insert was routed into the streaming path, which has no read timeout. A request is now identified as a subscription only when the caller supplies a callback
- Corrected the declared Python support to 3.10 and newer. The package was never importable on 3.9 — montycat.core.schema imports types.UnionType, added in 3.10 — so 3.9 installs failed at import despite the metadata advertising support
- Documented real-time subscriptions, TLS connections, owner and access management, and the response envelope, none of which had examples before
- The test suite now runs in CI against Python 3.10 through 3.13; the previous pipeline only performed a packaging dry run, so no test had ever executed there
- Completed the data-mesh governance documentation to match the Node, Dart, and Rust clients
July 28, 2026
- New governance layer: a superowner delegates six administrative capabilities — provision-keyspace, remove-keyspace, manage-snapshots, manage-semantic, manage-schema, and manage-access — to owners, scoped per store and per keyspace
- Authorization is deny-by-default and evaluated by the engine on every administrative operation; holding a governance capability never implies data access
- Optional qualifiers narrow a delegation further: permitted storage types (invalid for manage-snapshots, which is always in-memory) and permitted semantic models (valid for provision-keyspace and manage-semantic)
- An owner that creates a keyspace automatically receives its creator rights; explicit denials suspend those rights for a specific resource and outrank any broader grant
- Added policy inspection and audit: view effective policy, explain an authorization decision, review policy history, and export the active policy as JSON or YAML
- Added dry-run previews for grant and revoke, plus JSON/YAML policy manifests with validate, plan, and apply for repeatable governance as infrastructure as code
- Schema commands (create, change, preview, remove) are now gated by the manage-schema capability instead of requiring superowner credentials
- Fixed the ALL data permission: grant-to and revoke-from now honor the keyspaces argument, so ALL means read plus write on exactly the named keyspaces rather than silently applying store-wide; store-wide ALL and the on-disk credentials format are unchanged
- Revoking ALL for a whole store now clears the store from every permission map, leaving no residual read or write access
- Semantic search can now be enabled or disabled for a single store/keyspace rather than only DB-wide or per store; disabling a keyspace removes its configuration and drops its vectors
- Refreshed engine dependencies, including tokio, serde, regex, futures, and fastembed
July 28, 2026
- Added the complete governance policy lifecycle, dry-run previews, policy export, and JSON/YAML manifest operations
- Exported PolicyCapability, PolicyKeyspaceType, SemanticModel, and PolicyFormat from the crate root
- Added keyspace-scoped semantic enrollment and removal
- Added hybrid semantic search with metadata pre-filtering and optional min_score
- Added capability-aware policy qualifier validation and correct store-level provision-keyspace commands
- Hardened plain TCP operation when the crate is compiled with optional TLS support
- Expanded deterministic loopback coverage for Engine, governance, access, semantic, and operator wire contracts
July 28, 2026
- Added policy view, history, explain, grant, revoke, deny, remove-denial, preview, export, validation, planning, and application APIs
- Exported PolicyCapability, PolicyKeyspaceType, SemanticModel, and PolicyFormat from the public package
- Added keyspace-scoped semantic enrollment and removal with client-side scope validation
- Added capability-specific model and storage-type validation before policy commands reach the server
- Provision-keyspace policies are store-scoped, while manage-snapshots always targets in-memory keyspaces
July 28, 2026
- Added policy inspection, mutation, denial, preview, export, and JSON/YAML manifest APIs on Engine
- Exported PolicyCapability, PolicyKeyspaceType, SemanticModel, and PolicyFormat from the package root
- Added keyspace-scoped enableSemanticSearch and disableSemanticSearch operations
- Semantic model constraints apply to provision-keyspace and manage-semantic policies
- Storage-type constraints apply to provision, removal, schema, access, and semantic capabilities; manage-snapshots remains implicitly in-memory
- Provision-keyspace policy commands now correctly remain at store scope
July 28, 2026
- Added the complete data-mesh policy lifecycle: view, history, explain, grant, revoke, deny, remove-denial, previews, export, and JSON/YAML manifest validation, planning, and application
- Added PolicyCapability, PolicyKeyspaceType, SemanticModel, and PolicyFormat enums at the package root
- Added keyspace-scoped semantic enrollment and removal; specifying a keyspace without its store is rejected client-side
- Policy models apply only to provision-keyspace and manage-semantic; storage types apply to keyspace capabilities except manage-snapshots, which is always in-memory
- Provision-keyspace is store-scoped and therefore omits keyspace from policy commands
- Permission values are normalized before transmission, including Permission enum members and mixed-case strings
- Breaking: enable_semantic_search(model=...) now requires a SemanticModel enum value instead of a string
July 20, 2026
- Added semantic_filter to semantic_search: structured criteria are resolved through the existing lookup stack before vector retrieval
- HNSW search is restricted to the matching candidates, so results satisfy the metadata constraint while staying ranked by cosine similarity
- Empty metadata matches short-circuit before embedding the query, avoiding unnecessary model work
- Semantic filtering supports indexed fields, timestamps, pointers, and schemas through the same criteria model as lookup_keys
July 20, 2026
- Added semantic_search_get_keys_where and semantic_search_get_values_where for meaning-based retrieval constrained by indexed metadata, timestamps, and pointers
- Metadata is a hard AND pre-filter: it restricts candidates while cosine similarity remains the sole ranking signal
- Added optional min_score filtering for hybrid semantic queries
July 20, 2026
- Added semanticSearchGetKeysWhere and semanticSearchGetValuesWhere for meaning-based retrieval constrained by indexed metadata, timestamps, and pointers
- Metadata is a hard AND pre-filter: it restricts candidates while cosine similarity remains the sole ranking signal
- Added optional minScore filtering for hybrid semantic queries
July 20, 2026
- Added semanticSearchGetKeysWhere and semanticSearchGetValuesWhere for meaning-based retrieval constrained by indexed metadata, timestamps, and pointers
- Metadata is a hard AND pre-filter: it restricts candidates while cosine similarity remains the sole ranking signal
- Added optional minScore filtering for hybrid semantic queries
July 20, 2026
- Added semantic_search_get_keys_where and semantic_search_get_values_where for meaning-based retrieval constrained by indexed metadata, timestamps, and pointers
- Metadata is a hard AND pre-filter: it restricts candidates while cosine similarity remains the sole ranking signal
- Added optional min_score filtering for hybrid semantic queries
July 19, 2026
- Semantic search hits now use the {__key__, __score__, __value__} envelope, consistent with the __key__/__value__ wrapper lookup_values already returns; __key__ is always present, so a hit is never mistaken for user data. Replaces the previous {key, score, value} shape — a wire-breaking change for clients that read the old field names
- Index restore no longer logs a false "Missing index found" warning for empty volumes (the always-present custom-keys volume is created empty and needs no index until its first custom-key write)
July 19, 2026
- Semantic search hits now use the { __key__, __score__, __value__ } envelope — the same field names lookup_values returns with key_included — replacing { key, score, value }; get_keys hits are { __key__, __score__ }. Breaking for code that read the old key/score/value field names off the parsed payload
July 19, 2026
- Semantic search hits now use the {__key__, __score__, __value__} envelope — the same field names lookupValuesWhere returns with keyIncluded: true — replacing {key, score, value}; getKeys hits are {__key__, __score__}. Breaking for code that read the old key/score/value field names
July 19, 2026
- Semantic search hits now use the { __key__, __score__, __value__ } envelope — the same field names lookupValuesWhere returns with keyIncluded: true — replacing { key, score, value }; getKeys hits are { __key__, __score__ }. Breaking for code that read the old key/score/value field names
July 19, 2026
- Semantic search hits now use the {__key__, __score__, __value__} envelope — the same field names lookup_values_where returns with key_included — replacing {key, score, value}; get_keys hits are {__key__, __score__}. Breaking for code that read the old key/score/value field names
July 18, 2026
- New Semantic edition — built-in AI semantic (vector) search directly in the engine: on-device text embeddings and a per-keyspace HNSW index, with no external vector database or embedding API
- Enable or disable semantic search DB-wide (or scoped to a single store); choose from MiniLM, BGE-small (default), BGE-base, and multilingual E5 models, downloaded on demand
- Embedding runs off the write hot path in a background worker so writes stay fast; existing data is backfilled when semantic search is enabled
- Schemaless keyspaces can now add brand-new fields via update_value; keyspaces with an enforced schema stay strict
- Fixed removing an enforced schema from in-memory keyspaces when the schema had no records yet
- Expiration-check interval is now configured in whole seconds (consistent with the snapshot rate), with an automatic one-time migration of older config files
- Background timers now sleep in short, interruptible increments so graceful shutdown is never delayed
- Standardized the MONTYCAT_EXPIRATION_RATE environment variable (MONTYCAT_EXPIRATION_CHECK kept as a deprecated alias)
- Container entrypoint now runs the engine as PID 1, so docker stop performs a clean graceful shutdown and returns the correct exit code
July 18, 2026
- Added semantic search: enable_semantic_search / disable_semantic_search and semantic_search_get_values / semantic_search_get_keys for meaning-based retrieval (Semantic edition)
- Fixed update_value when addressing a record by custom key — the custom key is now hashed like every other operation (previously the server could not parse it)
July 18, 2026
- Added semantic search: enableSemanticSearch / disableSemanticSearch and semanticSearchGetValues / semanticSearchGetKeys for meaning-based retrieval (Semantic edition)
July 18, 2026
- Added semantic search: enableSemanticSearch / disableSemanticSearch and semanticSearchGetValues / semanticSearchGetKeys for meaning-based retrieval (Semantic edition)
July 18, 2026
- Added semantic search: enable_semantic_search / disable_semantic_search and semantic_search_get_values / semantic_search_get_keys for meaning-based retrieval (Semantic edition)
- Fixed creating persistent keyspaces with cache and compression — the cache size is now sent correctly (was rejected as an invalid request)
- Fixed update_cache_and_compression for the same reason
- Fixed remove_enforced_schema, which was sending an extra field and being rejected
July 13, 2026
- Introduced a persistent, file-based append-only write-ahead log (WAL) for indexing tasks with crash recovery
- Watermark management tracks the highest applied sequence number per queue, ensuring exactly-once index application after restarts
- Index synchronization for all CRUD operations with snapshot keyspace checks for consistency
- Added backpressure handling to connection and request processing paths to keep the server stable under heavy load
- Reduced data cloning across CRUD, indexing, and counting paths for lower memory overhead and faster operations
- Fixed WAL truncation on Windows by using a fresh write-mode handle
- Expanded unit test coverage, including WAL crash recovery and watermark behavior
May 12, 2026
- Implemented global key sequence indexing for all CRUD operations, enabling efficient paginated key retrieval across volumes
- Added per-volume key indexes with batch processing for fast lookups and reverse iteration support
- Integrated tikv-jemallocator for improved memory allocation performance and reduced fragmentation
- Refactored volume memory tracking with an "always forward" strategy and explicit target volume support
- Atomic inserts into persistent storage for improved write reliability
- Bulk delete now streams indexing tasks per chunk, preventing memory pile-up under large workloads
- Graceful shutdown with Unix signal handlers and interruptible sleep loops
- Enhanced TLS key parsing to support multiple certificate formats; TLS handshake moved into a spawned task
- Superowner credentials can now be configured via environment variables
- Subscription server integrated with RootInmemory for consistent in-memory access
- Enhanced store removal with detailed logging and proper cleanup for both persistent and in-memory stores
- Improved server logging with task and type details via server_info helper
May 12, 2026
- Fixed StoreRequestClient initialization where volumes and latest_volume fields were being ignored in get_bulk
- Refactored get_bulk validation to allow any valid combination of volumes, latest_volume, and limit
- Added optional subscription port parameter to the subscribe API
- Comprehensive unit test suite with 72 tests across all core modules
- Multi-platform CI/CD pipeline with GitHub Actions (Ubuntu, macOS, Windows)
May 12, 2026
- v1.0.5–1.0.8: Fixed cache values, connection errors, empty string insertion, connection timeout, bulk write, and bulk read
- Added volume-based retrieval in getBulk() for both persistent and in-memory keyspaces
- v1.0.9: Stateless query refactor — command and limitOutput moved from class fields to local method parameters
- subscribe() unified into the KV base class with optional subscriptionPort parameter, available on all keyspace types
- cache and compression parameters moved from class fields to method parameters in createKeyspace() and updateCacheAndCompression()
- Removed mutual-exclusivity restriction between withPointers and pointersMetadata
May 12, 2026
- Stateless query refactor: parameters now passed directly to convert_to_binary_query instead of being set as class attributes
- Improved bulk key handling using explicit concatenation for reliability
- Refined limit validation logic across generic_kv and persistent_kv classes
- Improved input validation for key retrieval and keyspace creation methods
March 31, 2026
- Added subscribe() method to GenericKV, now available on both persistent and in-memory keyspaces
- Removed mutual-exclusivity restriction between withPointers and pointersMetadata — both can now be used together
- Standardized key validation: all methods now throw when both key and customKey are provided, or when neither is provided
- Updated moduleResolution to "bundler" in tsconfig for improved compatibility
May 12, 2026
- Signal server now loads or registers a persistent token on startup before sending any reports
- Nodes that fail to obtain a token disable reporting gracefully instead of crashing
- Token refresh logic added to handle expired or rejected tokens during the reporting loop
February 5, 2026
- Improved schema enforcement and validation mechanisms
- Added support for nullable values in schema fields (if schema enforced on database level) to improve flexibility in data modeling
- Enhanced error handling and reporting across all client libraries
- Release currently available through apt repo and Docker Hub only. Will add binaries for download soon
February 5, 2026
- Allowed nullable values in schema fields (if schema enforced on database level) to improve flexibility in data modeling
- Improved connection handling and error reporting
February 5, 2026
- Allowed nullable values in schema fields (if schema enforced on database level) to improve flexibility in data modeling
- Improved connection handling and error reporting
February 5, 2026
- Allowed nullable values in schema fields (if schema enforced on database level) to improve flexibility in data modeling
- Improved connection handling and error reporting
February 5, 2026
- Allowed nullable values in schema fields (if schema enforced on database level) to improve flexibility in data modeling
- Improved connection handling and error reporting
November 4, 2025
- Introduced Montycat client libraries for Python, JavaScript, TypeScript, and Dart
- Improved Gossip protocol communication and stability
- Enhanced Proof of Stake consensus efficiency
- Optimized storage engine performance and reduced memory footprint
- Refined cluster synchronization and node discovery mechanisms
November 4, 2025
- Seamless integration with Flutter for cross-platform apps
- Fully async API with Stream support
- Lightweight and dependency-free runtime core
- Compatible with both mobile and desktop platforms
November 4, 2025
- Async support via asyncio for non-blocking operations
- Type hints and auto-completion for modern IDEs
- Native serialization/deserialization with Montycat binary format
- Integrated authentication and connection pooling
November 4, 2025
- Compatible with both ES modules and CommonJS
- Simple API for connecting, querying, and managing data
- Optimized for low-latency web and server communication
- Includes reconnect and retry strategies for resilient connections
November 4, 2025
- Full async API with Promise-based operations
- Lightweight, modular architecture with minimal dependencies
- Supports Node.js environments
- Integrated JSON and binary transport support
- Comprehensive type definitions for all API methods
