Skip to content

Releases: cortexdbai/cortexdb-releases

CortexDB v0.8.2

Choose a tag to compare

@pmalik pmalik released this 15 Jul 16:43
  • POST /v1/erasures erased the ENTIRE scope, ignoring the selector. The request carried no selector field, so a caller-supplied selector.memory_ids was silently dropped and the whole scope was permanently, irreversibly destroyed — returning 202. 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/erasures now honors selector.memory_ids (erasing ONLY the named records and their derived data), requires confirm_all: true for a deliberate whole-scope erasure (matching /v1/forget), rejects a typo'd selector key with 422, 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 metadata is accepted again. Pre-v0.8.0 clients that tagged events with a top-level metadata: {labels: [...]} object were hard-broken by the v0.8.0 strict envelope (422). That shape is accepted again and folded into context.labels. New clients should write context.labels directly.

  • Strict unknown-field rejection now covers /v1/recall, /v1/forget, the bulk request envelope, and budgets — a typo'd top-level key (e.g. temporl for temporal) returns 422 instead of silently dropping the temporal pin and answering with current-time data.

  • directives rejects unknown keys and validates ttl_for_belief_layer as an ISO-8601 duration (a typo no longer silently skips the directive).

  • Triple predicate is validated (non-empty, length-bounded) like subject/object ids.

  • CORTEX_EXTRACTION_BATCH_EVENTS startup logging corrected — the knob was always wired, but the log printed an unrelated fixed constant, making it look dead.

CortexDB v0.8.1

Choose a tag to compare

@pmalik pmalik released this 15 Jul 14:53
  • Cross-tenant authorization on scope-supplied routes. Several endpoints acted on the caller-supplied scope with 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:/data failed at first boot with Permission 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 rides context.labels. Connector insight detection was rewired off the retired /v1/entities route onto /v1/facts.

  • /v1/answer temporal valid_during — a query with only a valid_during window 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 response as_of reflects the window.

  • CLI: forget --query now works (was silently dropped → confusing 422); bulk writes warn on a 207 partial commit instead of reporting success; --ordering help 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 surfaces results / failures / partial.

  • GETTING_STARTED recall example uses budgets.max_tokens (the prior tokens_total was silently ignored) and the correct Cortex SDK class name. API reference: context.observed_at and idempotency_key corrected to optional; idempotency replay is signaled by a response body flag, not a header.

CortexDB v0.8.0

Choose a tag to compare

@pmalik pmalik released this 15 Jul 10:47
  • The write envelope (POST /v1/experience, bulk items) is now strict: unknown or misplaced top-level and context fields return 422 instead of being silently ignored. Clients that were unknowingly sending typo'd or misplaced fields (most commonly observed_at at the top level, which silently lost the occurrence timestamp) will now get a clear validation error. As part of this, top-level observed_at is accepted as a first-class, validated alias for context.observed_at — the common placement now works correctly instead of corrupting the bitemporal timeline.

  • POST /v1/experience/bulk responses changed: accepted and event_ids now count distinct persisted events (idempotency-deduplicated items are no longer double-counted), and a new index-aligned results[] array reports each item's event_id and replayed_from_idempotency. A batch where some items committed and some failed returns 207 Multi-Status (partial: true, per-index failures[]); any 4xx/5xx bulk 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 returns 409 from either endpoint. Import sources remain separate buckets.

  • Reusing an idempotency key with the same content but different context.labels is now reliably a 409 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 ci workflow is now a required merge check on main.

CortexDB v0.7.9

Choose a tag to compare

@pmalik pmalik released this 14 Jul 20:01
  • 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 same idempotency_key — even under one deployment API key — no longer collide: the second tenant's write is accepted normally instead of failing with 409 IDEMPOTENCY_CONFLICT, and a conflict response can no longer reveal another tenant's event_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/answer now honors temporal.as_of (and the as_of_valid / as_of_recorded axis 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's as_of echoes the requested instant instead of the current time. Note that as_of pins both bi-temporal axes ("true at T" and "known at T") — use as_of_valid to ask what was true at T regardless of when the system learned it.
  • GET /v1/experience/status accepts an optional scope= parameter for an exact tenant-scoped probe by idempotency_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.text is rejected up front with 422 INVALID_ENVELOPE — previously it was accepted and then surfaced as 502 INDEXING_FAILED after the write was already durable.
    • Unsupported pagination parameters on POST /v1/recall (limit, offset, page, cursor, page_size) return 422 with guidance (use budgets to bound the pack; GET /v1/events to paginate raw records) instead of being silently ignored.
    • An empty filter value list (e.g. filters.metadata.labels: []) returns 422 instead of silently matching nothing — a footgun for dynamically-built filters.

Fixes the two remaining findings (and the minor validation gaps) from the v0.7.8 external bug report.

CortexDB v0.7.8

Choose a tag to compare

@pmalik pmalik released this 14 Jul 15:51
  • 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_tokens from 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

Choose a tag to compare

@pmalik pmalik released this 14 Jul 15:01
  • 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 (default 20): 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. Set 0, false, or off to 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" or directives.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=consolidation label, making them queryable (GET /v1/events?labels=source=consolidation) and auditable instead of appearing as anonymous events in the scope.

CortexDB v0.7.6

Choose a tag to compare

@pmalik pmalik released this 14 Jul 13:55

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

Choose a tag to compare

@pmalik pmalik released this 14 Jul 05:06

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

Choose a tag to compare

@pmalik pmalik released this 13 Jul 18:32

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

Choose a tag to compare

@pmalik pmalik released this 13 Jul 17:41

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=Pro into account_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-temporal as_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.