Releases: cortexdbai/cortexdb-releases
Release list
CortexDB v0.8.2
-
POST /v1/erasureserased the ENTIRE scope, ignoring the selector. The request carried noselectorfield, so a caller-suppliedselector.memory_idswas silently dropped and the whole scope was permanently, irreversibly destroyed — returning202. An empty, absent, or typo'd selector all wiped the scope, on the very endpoint operators are told to use for GDPR erasure requests./v1/erasuresnow honorsselector.memory_ids(erasing ONLY the named records and their derived data), requiresconfirm_all: truefor a deliberate whole-scope erasure (matching/v1/forget), rejects a typo'd selector key with422, and reports the true deleted count. A GDPR request targeting one subject no longer destroys the tenant. -
Cluster mode silently disabled ~half the API. The distributed coordinator implemented only 8 of 75 storage methods and fell through to inert defaults for the rest — so on a multi-node deployment, layer reads returned empty for locally-owned data, fact/selective deletion no-op'd, GDPR forget-tombstones were not durable (erased events resurrected on restart), usage/billing reported zero, and the index-audit always reported "clean". All storage operations now execute against the local engine; a delegation-completeness test prevents recurrence.
-
Back-compat: top-level
metadatais accepted again. Pre-v0.8.0 clients that tagged events with a top-levelmetadata: {labels: [...]}object were hard-broken by the v0.8.0 strict envelope (422). That shape is accepted again and folded intocontext.labels. New clients should writecontext.labelsdirectly. -
Strict unknown-field rejection now covers
/v1/recall,/v1/forget, the bulk request envelope, andbudgets— a typo'd top-level key (e.g.temporlfortemporal) returns422instead of silently dropping the temporal pin and answering with current-time data. -
directivesrejects unknown keys and validatesttl_for_belief_layeras an ISO-8601 duration (a typo no longer silently skips the directive). -
Triple
predicateis validated (non-empty, length-bounded) like subject/object ids. -
CORTEX_EXTRACTION_BATCH_EVENTSstartup logging corrected — the knob was always wired, but the log printed an unrelated fixed constant, making it look dead.
CortexDB v0.8.1
-
Cross-tenant authorization on scope-supplied routes. Several endpoints acted on the caller-supplied
scopewith no membership check, while their read/write siblings (recall, events, by-id reads) correctly enforced scope-membership roles. On deployments using per-actor token auth (PASETO/JWT via the minter), any authenticated caller could target another tenant''s scope — one that tenant had written to (and thus auto-registered) — to delete/erase its data (/v1/forget,/v1/erasures), export its memories (/v1/export), take over the scope by rewriting its member roster (PUT /v1/scopes/members), delete/enumerate registrations (/v1/scopes,/v1/scopes/prune), revoke another caller''s token (/v1/auth/revoke), or read cross-tenant fact timelines (/v1/facts/timeline). All of these now enforce the same owner/reader membership checks as the rest of the surface. Deployments running with auth disabled, or with only the static operator key, were never exposed (the operator key is a deployment-wide secret by design, not a per-actor credential). Token-auth deployments should upgrade. -
docker run -v ./data:/datafailed at first boot withPermission denied(a host bind-mount is root-owned while the server runs as uid 1000). The container now chowns the data directory at startup (via a privilege-dropping entrypoint) so the documented Docker quickstart works out of the box.docker run --user <uid>continues to work unchanged. -
Ingestion connectors (Slack, Jira, Notion, and every connector using the shared framework) were broken by the v0.8.0 strict write envelope: they emitted
context.thread_id/meta/preceded_by, which now return 422 and stalled the sync cursor. This metadata now ridescontext.labels. Connector insight detection was rewired off the retired/v1/entitiesroute onto/v1/facts. -
/v1/answertemporalvalid_during— a query with only avalid_duringwindow was treated as un-pinned, so derived-layer context was injected without applying the requested window. It is now a proper temporal pin, and the responseas_ofreflects the window. -
CLI:
forget --querynow works (was silently dropped → confusing 422); bulk writes warn on a207partial commit instead of reporting success;--orderinghelp no longer promises unimplemented enforcement. -
MCP: erasure dry-run now reports the real affected count (was always 0).
-
Python SDK README
forget()example corrected (reason=); TypeScript bulk response type now surfacesresults/failures/partial. -
GETTING_STARTED recall example uses
budgets.max_tokens(the priortokens_totalwas silently ignored) and the correctCortexSDK class name. API reference:context.observed_atandidempotency_keycorrected to optional; idempotency replay is signaled by a response body flag, not a header.
CortexDB v0.8.0
-
The write envelope (
POST /v1/experience, bulk items) is now strict: unknown or misplaced top-level andcontextfields return422instead of being silently ignored. Clients that were unknowingly sending typo'd or misplaced fields (most commonlyobserved_atat the top level, which silently lost the occurrence timestamp) will now get a clear validation error. As part of this, top-levelobserved_atis accepted as a first-class, validated alias forcontext.observed_at— the common placement now works correctly instead of corrupting the bitemporal timeline. -
POST /v1/experience/bulkresponses changed:acceptedandevent_idsnow count distinct persisted events (idempotency-deduplicated items are no longer double-counted), and a new index-alignedresults[]array reports each item'sevent_idandreplayed_from_idempotency. A batch where some items committed and some failed returns207 Multi-Status(partial: true, per-indexfailures[]); any4xx/5xxbulk response now guarantees nothing was committed — whole-batch retries are safe. -
Selector-scoped fact deletion now works on single-node deployments. Since v0.7.5, five coordinator methods (
get_fact,delete_derived_by_source,delete_derived_facts,delete_derived_by_attributes,delete_facts_in_scope) were served as inert defaults through the production wrapper — fact-targeted forgets silently deleted nothing. If you issued fact-selector forgets on v0.7.5–v0.7.9, re-run them on v0.8.0. -
Bulk envelope validation is atomic: every item is validated before anything commits, matching the schema-error path.
-
Single and bulk experience writes share one idempotency bucket: the same
(scope, key, body)replays across endpoints instead of double-storing; a different body under a reused key returns409from either endpoint. Import sources remain separate buckets. -
Reusing an idempotency key with the same content but different
context.labelsis now reliably a409 IDEMPOTENCY_CONFLICT(labels are part of the write identity), never a silent replay that drops the new labels. -
CI gate repaired end to end (embedded-docs parity, TypeScript SDK suite actually executing, runner disk headroom) and the
ciworkflow is now a required merge check onmain.
CortexDB v0.7.9
- Idempotency keys are now namespaced per tenant. The dedup scope is
(scope root, caller, endpoint family, key), where the scope root is the top-level segment of the write's scope path (org:acct_46,user:alice). Two tenants reusing the sameidempotency_key— even under one deployment API key — no longer collide: the second tenant's write is accepted normally instead of failing with409 IDEMPOTENCY_CONFLICT, and a conflict response can no longer reveal another tenant'sevent_id. Within a tenant nothing changes: an identical retry still replays the original event, and reusing a key with a different body is still a 409. Upgrade note: idempotency records written by earlier versions do not carry across this upgrade — a retried pre-upgrade write re-processes (safely) instead of replaying. POST /v1/answernow honorstemporal.as_of(and theas_of_valid/as_of_recordedaxis pins). Point-in-time questions are answered from the state at the pinned instant: retrieval, the evidence block, and the answer prompt all reflect the pin, and the response'sas_ofechoes the requested instant instead of the current time. Note thatas_ofpins both bi-temporal axes ("true at T" and "known at T") — useas_of_validto ask what was true at T regardless of when the system learned it.GET /v1/experience/statusaccepts an optionalscope=parameter for an exact tenant-scoped probe byidempotency_key; without it, the probe searches all of the calling actor's own tenants (never another caller's).- Clearer validation errors:
- An empty (or whitespace-only)
content.textis rejected up front with422 INVALID_ENVELOPE— previously it was accepted and then surfaced as502 INDEXING_FAILEDafter the write was already durable. - Unsupported pagination parameters on
POST /v1/recall(limit,offset,page,cursor,page_size) return422with guidance (usebudgetsto bound the pack;GET /v1/eventsto paginate raw records) instead of being silently ignored. - An empty filter value list (e.g.
filters.metadata.labels: []) returns422instead of silently matching nothing — a footgun for dynamically-built filters.
- An empty (or whitespace-only)
Fixes the two remaining findings (and the minor validation gaps) from the v0.7.8 external bug report.
CortexDB v0.7.8
- Exhaustive and metadata-filtered recall packs no longer lose their newest events to the default token budget. The cross-layer knapsack (introduced in v0.7.6) applied its default 4,000-token budget to the events layer of complete-corpus packs and evicted from the tail — which, on a chronological corpus, deleted the newest events. With grounded citations enabled, the model could neither see nor cite the latest entries of a changelog-style corpus even though the context block carried them. Complete-events packs are now exempt from default-budget event eviction; derived layers still drain toward the budget, and an explicit
budgets.max_tokensfrom the caller still applies to every layer.
This completes the fix started in v0.7.7: v0.7.7 corrected the citation renderer (recency window, byte-budget direction, marker parsing), and v0.7.8 corrects the upstream pack assembly so the renderer actually receives the newest events it is meant to protect.
CortexDB v0.7.7
-
Grounded
[S#]citations on exhaustive (whole-scope) packs now always include the newest sources: both the source-count window and the prompt byte budget trim from the oldest side of a chronological pack. Previously, "what is the latest X?" could be answered from the middle of a chronological corpus because the newest chunks fell outside the cited window even though retrieval had returned them. -
Citation markers above the source cap now parse correctly. Recency windows can legitimately start above
[S1], and a model citing the newest source in a large pack no longer has that citation silently dropped from the response. -
CORTEX_ANSWER_MAX_CITED_SOURCES(default20): per-deployment cap on the grounded-citation source window, for corpora whose answers legitimately draw on more than 20 chunks (for example, a full release history). -
CORTEX_CONSOLIDATION_ENABLED(default on): first-class switch for background memory consolidation. Set0,false, oroffto disable it entirely — previously the only way to stop consolidation was to raise its thresholds out of reach. -
Consolidation now skips curated content automatically: events written with
context.intent: "documentation"ordirectives.extract: []are never folded into summaries. Summaries of curated chunks go stale the moment the source documents change, and consolidation archives the sources it folds in — documentation corpora should never pay that trade. -
Consolidation summaries now carry a
source=consolidationlabel, making them queryable (GET /v1/events?labels=source=consolidation) and auditable instead of appearing as anonymous events in the scope.
CortexDB v0.7.6
Closes every open finding from the v0.7.5 external report (DISC-006 through DISC-028):
- forget: fact ids and attribute selectors (about_subject / about_entity / predicate) now delete the derived records they name, with belief/concept cascade and accurate all-layer matched counts; a selector matching nothing says so explicitly. Superseded facts stay fetchable — GET /v1/facts/{id} is new, and as-of queries return the facts that were true then.
- beliefs: genuine same-key conflicts (support_tier, plan, status, …) reconcile to stance:contradicted; per-fact cardinality metadata can mark any predicate single- or multi-valued.
- deterministic recall: pack composition is now reproducible for a fixed query and store — derived-layer listings and rankers use total orderings instead of hash-map iteration order.
- typed triples: deterministic writes are excluded from LLM enrichment (no more duplicated or hallucinated facts), and ?wait=consolidated resolves immediately for them; write status reports the furthest completed stage, consistent with stages_completed.
- erasure: preview and completion counts now match the records actually deleted, including facts.
- security: a keyless boot on a network-exposed bind (the Docker quickstart) generates and persists an API key on first start instead of serving an open store; CORTEX_INSECURE_NO_AUTH=1 is the explicit opt-in for open dev mode.
- ollama: the native answer provider works out of the box (the default URL now targets Ollama's OpenAI-compatible /v1 surface; bare daemon addresses are normalized).
- events: /v1/events and /v1/events/{id} return one canonical body for the same id.
- performance: default embedding concurrency raised 4→8 and the write admission cap 64→128, recovering bulk-write throughput without touching the WAL fsync durability default.
Also included, disabled by default: fact-as-key retrieval expansion and question-shape-routed layer injection (CORTEX_FACT_INDEX_KEYS, CORTEX_LAYER_INJECTION_GATE) — bench-validated, awaiting production routing hardening.
No breaking changes.
CortexDB v0.7.5
Fixes for four issues reported against v0.7.4:
- forget: cascade=derived_only with a memory_ids selector now deletes exactly the derived records (facts, beliefs, episodes, concepts) traced to the selected events — never the whole layer. A whole-layer wipe requires an explicitly empty selector plus confirm_all:true. Deleted counts in the response are now accurate, and wiping a derived layer no longer touches event storage.
- experience: concurrent writes with the same idempotency_key now collapse to a single event; retried and racing requests all receive the winning event_id. Applies to both single and bulk writes, and composes with the persistent idempotency store introduced in v0.7.4.
- enrichment: entity extraction no longer silently contributes an empty graph on large, dense documents. The output cap is configurable (CORTEX_ENTITY_MAX_TOKENS, default 4096), truncated responses retry automatically at a higher cap, and internal provider limits no longer clamp dense fact extraction.
- concepts: every concept now carries a walkable provenance trail — supported_by links the facts and beliefs that fed synthesis, and synthesis_inputs.layers reports only layers that actually contributed.
No breaking changes. All response-shape additions are backward compatible.
CortexDB v0.7.4
This release merges the recall-quality workstream on top of the v0.7.3 security patch. It closes a silent index-loss class, hardens recall scope/temporal correctness, adds index self-audit + repair, and lands cross-layer budgeting and de-crowding — validated to move the full LongMemEval-S server run up from the prior baseline with no regression.
INDEX INTEGRITY (the headline)
- Fixed a silent search-index loss: a derived-layer forget with an empty selector and confirm_all could strip a scope's BM25/vector documents while the WAL kept the events, so recall went blind on already-stored memories with no error. Derived-layer forgets now purge only derived state and never touch event documents.
- New WAL<->index self-audit: the server can reconcile every durable event against the search indexes and repair any that are missing. Exposed as POST /v1/admin/index-audit (diagnostics.read; {"repair": bool}) and run report-only at startup (CORTEX_INDEX_AUDIT_STARTUP, default on). Filtered/exhaustive recalls now flag divergence between the listing and the ranked pool in diagnostics, so this failure mode can never be silent again.
- Enrichment scanner: fixed a cursor-promotion bug that could mark un-applied events done across a restart, and a livelock that drained only one chunk per boot.
RECALL CORRECTNESS
- Holistic recall at a member (leaf) scope no longer leaks sibling-member content; holistic/descend at a parent scope now blends member data upward (self + ancestors + strict descendants). Current-state answers no longer return a superseded belief when no as_of is given (bi-temporal as_of/valid-time queries unchanged).
- Exhaustive recall no longer treats a scan-capped page as "drained" (no more silently-truncated exhaustive blocks); graph and sibling channels enforce scope isolation.
RANKING + BUDGETS
- budgets.max_tokens is now a real cross-layer token budget (priority spend across layers, tail eviction, diagnostics.knapsack_evictions) — on by default; set CORTEX_CROSS_LAYER_KNAPSACK=0 to restore the prior behavior.
- Per-document pack cap (default on): stops one multi-chunk document from flooding the pack on documentation corpora; a no-op on conversational memory. Optional exact-title topic boost and derived-layer embedding rerank ship default-off (CORTEX_TOPIC_MATCH_BOOST, CORTEX_DERIVED_EMBEDDINGS).
PERFORMANCE
- Recall read path: removed a per-request O(corpus) WAL clone, a term-matching allocation storm, and a serialized retrieval stage; fixed a BM25 group-commit reader race. Warm p95 on a normal-sized corpus improved ~36%. Recall now emits per-phase timings in diagnostics.time_ms (recall.) for latency attribution. NOTE: on very large corpora under heavy concurrency the dominant recall cost is external calls (query embedding, reranker, query-expansion LLM) — the target of a follow-up performance pass.
UPGRADE NOTES
- No storage-format changes; volumes migrate in place from v0.7.x.
- The cross-layer knapsack and per-document cap are ON by default; both have kill switches (CORTEX_CROSS_LAYER_KNAPSACK=0, CORTEX_PACK_PER_DOC_CAP=0). Both were validated non-regressing on the eval corpora.
- Includes all v0.7.3 security fixes (cross-tenant IDOR on by-ID routes, triple-enrichment integrity, extraction parsing).
CortexDB v0.7.3
This is a security and correctness patch. It closes a cross-tenant data-disclosure class on by-ID fetch routes, stops the direct-fact path from corrupting deterministic truth, and fixes recall scope resolution so member data neither leaks sideways nor goes missing.
SECURITY — CROSS-TENANT ISOLATION (please upgrade)
- Direct by-ID fetch routes (GET /v1/events/{id}, /v1/understanding/{id}, /v1/erasures/{id}, /v1/understanding/synthesize/{id}, the /v1/lifecycle by-id routes, /v1/blobs/{id}, /v1/import/{id}) resolved a record without a caller-supplied scope, so the scope authorization that /v1/recall enforces never ran. An authenticated caller could read another tenant's record by id. Every by-ID and direct-fetch route now performs object-level authorization against the resolved record's own scope (or, for scope-less staged objects like blobs and import jobs, against its owner). A caller with no access receives 404 — the endpoint never confirms that another tenant's id exists. The by-id write twins (erasure cancel, lifecycle memory-event cancel) authorize before mutating.
DETERMINISTIC-FACT INTEGRITY
- Typed triples written through the synchronous direct-fact path are no longer also run through async LLM enrichment. Re-enriching an already-deterministic fact re-derived it from prose and could hallucinate adjacent attributes (an observed case turned a known-good
plan=Prointoaccount_status=active). Triple-origin events are now skipped by the enrichment scanner.
ENTITY EXTRACTION (Anthropic and other providers)
- Knowledge extraction no longer stalls at facts=0/beliefs=0 when the LLM wraps its JSON reply in markdown code fences or surrounds it with prose (which some providers do even when a JSON schema is requested), and no longer truncates large batched extractions. The parser now recovers the JSON payload from a fenced/prose reply on both the batched and per-event paths, and batches get a larger token budget. Completes the bug_03 series (v0.7.0 response_format type -> v0.7.1 json_schema strict field -> v0.7.3 lenient parsing + batch budget).
RECALL SCOPE + TEMPORAL CORRECTNESS
- Holistic recall at a member (leaf) scope no longer pulls in sibling members' private content. Holistic resolution is now self + ancestors + strict descendants: a leaf has no descendants, so it can never surface a sibling (siblings are descendants of the shared parent, not of the leaf).
- Holistic/descend recall at a household (parent) scope now blends member data upward (member beliefs and events surface at the parent) — previously these questions abstained even though the data existed one scope down. The two fixes are one model, so they cannot reopen each other.
- Current-state answers (no
as_of) no longer return a superseded value: when no time is pinned, recall keeps only currently-valid beliefs/facts (valid_to = null or in the future). Bi-temporalas_of/ valid-time queries are unchanged — the guard is a no-op whenever a time pin is present.
UPGRADE NOTES
- No storage-format changes; volumes migrate in place from v0.7.0–v0.7.2.
- Behavior change to be aware of: a by-ID fetch of a record you cannot access now returns 404 instead of 200. Integrations that fetched cross-tenant ids (which should not have worked) will see 404.
- If you self-host with auth disabled (dev_local) or a shared static API key, object-level authorization is not enforced (identities are operator-chosen) — this matches the existing membership model and is unchanged.