CortexDB 0.9.3 — GDPR erasure integrity, audit hash chain, temporal phrases (connectors 0.2.14)
Bug-fix and hardening release driven by the KAN board triage: 129 open issues were investigated against the codebase, ~75 verified server and connector defects were fixed, and every ticket got an evidence-backed disposition. No storage format changes, no migration: 0.9.x data directories work as-is (new audit-chain and vocabulary fields are serde-defaulted; legacy records replay cleanly).
GDPR / erasure integrity (the headline)
forget cascade=derived_onlyactually cascades (KAN-9/28/33/37/67): callers sendinglayers:["events"]previously zeroed out the derived cascade set, leaving episodes/facts/beliefs/understanding containing erased text. Derived layers explicitly named still narrow the cascade; when none is named, all four derived layers are cascaded.- Erasure previews honor the selector (KAN-6/30/38/69):
/v1/erasures/previewaccepted only{scope}and estimated the whole scope for deletion regardless of any selector. It now takes the same selector as execute (deny_unknown_fields), dry-runs the real derived match, and returns a per-layerestimated_affectedbreakdown. - Two-phase forget (KAN-12): new non-destructive
POST /v1/forget/previewreturns afpvw_preview id + per-layerestimated_deleted;/v1/forgetvalidatesfrom_preview_id. - Erased data cannot resurface via time-travel (KAN-11/70/71): the
recall engine drops episodes/concepts whose cited source events no
longer resolve — unconditionally, and before temporal gating, so
as_ofpins can't serve pre-erasure snapshots of erased content. - Event-derived concepts are reachable by the cascade (KAN-10):
SupportedBygained aneventsprovenance field, populated by the concept reflector and matched by the deletion cascade. - Reference scrubbing covers labels (KAN-32): surviving events lose
supports:<erased_id>labels, not justpreceded_byreferences.
Audit: tamper-evidence is now real
- Request bodies are actually hashed (KAN-41): the audit middleware
hashed empty bytes for every request, making
/v1/audit/verifyvacuous. It now buffers and hashes the raw wire bytes (multipart, oversize, and streaming bodies get explicitunhashed:markers).POST /v1/forgetappends a realgdpr=trueaudit row (was minting a throwaway id). - Hash-chained rows (KAN-26/39/68):
row_hash = sha256(prev_hash || canonical_json(row))computed on append; newGET /v1/audit/chain(head hash, length) andPOST /v1/audit/chain/verify(reportsfirst_broken_indexon tamper, deletion, or splice). Legacy JSONL rows are tolerated as a prefix. - Object bodies verify (KAN-72):
/v1/audit/verifyaccepts a raw string (hashed byte-for-byte) or a JSON object (canonical form: byte-sorted keys, no whitespace).
Temporal: the phrases people actually type
temporal.natural (KAN-7/36/54/55/62/63/65/66) now parses: bare
last week/month/year, N <unit> ago including word numbers
(two weeks ago, a month ago), last/this quarter, sprint phrases
(two sprints ago, documented 14-day convention), early/mid/late
period thirds (early last week, including with a leading noun phrase),
and everything cortex-bitemporal's date grounder handles (seasons,
written months, bare years) via fallback. Bi-temporal correctness fixes
ride along: episode recorded-axis pins gate on recorded_from (not
started_at — late-delivery episodes no longer leak into as_of
snapshots, KAN-43), and backdated/imported events are visible at
historical pins via source_recorded_at (KAN-27/35).
New / completed API surfaces
- Import (KAN-25/58/59/64): generic
POST /v1/import; export→import roundtrips work (import falls back to theMemoryEventV1export shape, preserving bi-temporal history viasource_recorded_at); import jobs emitimport_progress/import_error/import_completelifecycle events; the 202 carriesaccepted/failed/first_error/status_url. - Beliefs (KAN-31/42/44):
POST /v1/beliefscreates caller-declared beliefs with event/fact supports;/v1/beliefs/buildreturns full diagnostics instead of a bare count; belief-shaped events materialize deterministically without LLM triples; superseded beliefs close (valid_to+Deprecated) and are filtered from recall. - Vocabularies (KAN-8/34/51/61): terms with
definition/aliases/active/valid_from/valid_to/replaced_by persist and
round-trip; unknown fields 422 instead of silently dropping;
PATCH /v1/vocabularies/{name}; per-tenant isolation (closes an unfiled cross-tenant overwrite hole); schema published at/v1/schemas/vocabulary.
Server hardening
- Container auth hole closed (KAN-74/85):
CORTEX_BIND_ALL=1(set in the Docker image) defeated the loopback check, so a plaindocker runserved 0.0.0.0 with auth disabled. Loopback detection now derives from the resolved bind address, fails secure, and keyless container boots generate a key. Startup logs report bind/loopback/auth posture. - Scope-creator lockout fixed (KAN-60):
POST /v1/scopeswith an empty roster seeded a scope its own creator couldn't read. The creator is now seeded as Owner; owner-less rosters are rejected. --help/--version(KAN-76): short-circuit with zero side effects (previously booted the full server and created data directories).- No unbounded waits (KAN-40): the legacy coalescer path's ack wait is capped at 30s like the unified path.
Connectors 0.2.14
- Discord: text-less messages (stickers, polls, untitled embeds, system messages) are rendered or skipped instead of being sent as empty envelopes the server permanently 422s (20 tickets, one cause).
- Confluence: pages are enumerated via the read-your-writes content REST API instead of the eventually-consistent CQL search index, so just-created pages are found in their own sync run (9 tickets).
- Notion: comments on archived parent pages are ingested; comment fetch failures are logged instead of swallowed (KAN-24).
- Google Workspace: subject-less, bodyless Gmail messages are skipped client-side.
- Teams: docstring no longer overclaims transcripts/file shares (KAN-77).
Verification
Workspace cargo check clean; ~2,050 Rust tests and 245 connector tests
passing, including new regression tests pinning every fixed repro shape
(the layers:["events"] cascade gate, the preview selector, the
middleware body hash, the episode recorded-axis pin, the archived-parent
Notion comment, and the Discord content policy among them).