Memory at a glance
What each space holds
| Namespace | Summary | Memories |
|---|---|---|
| Loading… | ||
Calls, health & latency per tool
| Tool | Calls | Errors | p50 | p95 |
|---|---|---|---|---|
| Loading… | ||||
Memories per type
| Type | Memories | Share |
|---|---|---|
| Loading… | ||
Most active users
| User | Search | Add | Delete | Other | Total | Last seen |
|---|---|---|---|---|---|---|
| Loading… | ||||||
How Cortex works
How a memory is stored
A memory is the unit of storage — one verbatim item with its provenance. Every memory is filed on three independent axes: a namespace (where it lives), a type (what shape it is), and its topics (what it’s about). The axes are orthogonal — a memory carries exactly one namespace, exactly one type, and any number of topics.
Namespace × Type × Topics
Where it lives — and who can read it. The security boundary: recall is scoped to the namespaces you can access, enforced at the database (row-level security).
- Cardinality: exactly one, required on every memory_add.
- Kinds: user: (private) · org: (company-wide) · team: (a team).
- Closed by config — an admin provisions namespaces and grants access.
What shape the memory is. A closed, admin-configurable vocabulary the caller picks from — Cortex never classifies. Exposed on every search result and usable as a filter (matched by name only).
- Cardinality: exactly one, required on every memory_add.
- Default set: five shapes, each with a description (edit on the Prompt tab).
- Stored as metadata.type; a filter, never an access or ranking decision.
What the memory is about. An open, agent-coined organisation axis — free-form slugs, reused across memories. Topics organise and filter; they never grant access.
- Cardinality: zero or many — a memory can carry several at once.
- Open — reuse an existing slug (memory_list_topics) before coining one.
- Powers “list every X” via a facet-fetch over a topic.
Fields of a memory
| Field | Axis / role | Notes |
|---|---|---|
| namespace | access | Where it lives; the security boundary. Required. |
| type | shape | One of the closed vocabulary (in metadata.type). Required. |
| topics[] | subject | Open, multi-valued organisation axis. |
| content | payload | The verbatim text (Layer 1). |
| author · source | provenance | Who wrote it (bound to the caller) and where it came from. |
| created_at · valid_at | bitemporal | When recorded vs. when true — drives currency & supersession. |
| superseded_by | lifecycle | Set when a newer memory replaces this one (soft-delete). |
| content_hash | dedup | Exact-duplicate signal on write. |
| embedding | vector | 768-d, backfilled asynchronously for semantic recall. |
Two layers. The memory above is Layer 1 — stored verbatim and recallable immediately. A background cognify worker distils it into Layer 2: facts (extracted statements) and a knowledge graph (entities + relations), which inherit the memory’s namespace and power multi-hop recall via follow_entity. See the Architecture tab for the write and search paths.
Query the memory
Run memory_search exactly as a calling agent would — scoped to the namespaces you can access. Recall-first: leave namespaces blank to search everything you can read.
People
A user holds a bearer token and reaches their own user: space, the company-wide org: space (shared with everyone by config), plus any team they're added to.
| User | Role | Member of | |
|---|---|---|---|
| Loading… | |||
Teams
A team is a shared team: namespace; membership is a grant pointing at it.
Access matrix
Rows are principals, columns are shared namespaces. Click a cell to grant or revoke — the change applies immediately.
Authentication
How MCP clients authenticate to Cortex. Changes apply immediately, with no restart — an OAuth config is verified against the provider before it goes live, so a bad setting is rejected and the current mode stays up. Your /admin login is separate, so a change here can never lock you out of this console.
No authentication. Every request is treated as the local admin identity. For single-user or air-gapped dev only — never expose this on a shared network.
Long-lived bearer tokens, one per person. Issue and revoke them on the People tab — each token maps to a subject and reaches that subject's namespaces via the Access matrix.
Validate JWTs from a standard OIDC provider (Microsoft Entra, Okta, Auth0, Keycloak). Cortex is a resource server — it verifies tokens; the login/redirect flow lives in your provider and the MCP client.
⚠ Google / GCP OAuth is not supported (opaque access tokens, no dynamic client registration, no groups claim). Use Entra, Okta, Auth0, or Keycloak.
SSO login
Browser "Sign in with Microsoft" for this console (authorization-code + PKCE). Separate from MCP auth above — it needs a confidential OIDC client with a secret. The username/password admin from docker .env always stays as a break-glass fallback.
Base system prompt
The guidance Cortex sends every MCP client on connect — how calling agents should use the store. The per-caller accessible namespaces line and the team guidance (Rules tab) are appended automatically, so leave those out here. Changes take effect on a client's next connect (/mcp reconnect).
Type vocabulary
The shapes a memory can have — the valid values for the type field and the memory_search type filter. Each type has a lowercase-slug name and a one-line description; the description is surfaced to the calling agent (via memory_list_types and the connect instructions) so it picks the right shape without guessing from the name. type is REQUIRED on every memory_add and chosen by the calling agent — Cortex does not classify. Adding a type here makes it available immediately (a caller can use it with no code change). Removing one leaves existing memories with that label untouched (unfiltered search is unaffected); a filter on a removed value fails loudly.
Prompt-injection rules
Guidance the server prepends when a matching namespace is read, hardening downstream callers against injected instructions in stored content. Scope * applies everywhere; an exact namespace (org:rd) matches only it; a kind:* scope (user:*, team:*, org:*) matches every namespace of that kind.
| Priority | Scope | Text | |
|---|---|---|---|
| Loading… | |||
Collector schedule
One daily start time for the whole collector. When enabled, every enabled source below runs once a day at this time (evaluated in the console display timezone), one after another. Disable to pause all scheduled collection.
Collector sources
The data sources the collector runner ingests — a name, a target namespace, the
MCP tools it may use, and a plain-language scope instruction. Edit them here; the runner
reads them over HTTP with its token (no DB access). SINCE in the scope is replaced
with the window start. Seed from the shipped file manifests with collect.py --seed.
MCP connections
Register an outbound MCP server a collector can reach — the client's own data sources, held in this Cortex instance instead of a Claude account. Test connects to it (Cortex as a pure Go MCP client, no Claude) and lists its tools. POC: API-token (bearer) only, stored unencrypted; OAuth is a later phase.
How the team uses Cortex
Write path
how memories become searchableRead path
how a search is answeredSearch quality vs. corpus size
iLabel-free quality signals from every search — no query text is stored. One point per day; dot size = that day's search count, so a tiny dot is a low-sample day whose average is noisy — don't read a trend from it. The dashed amber line (right axis) is corpus size, so a rising abstention/miss rate alongside it would flag recall drift as the store grows.
KG growth over time
iCumulative Layer-2 entities (nodes) and relations (edges) the worker has extracted. Flat stretches while memories keep growing can signal the extractor stalling or falling behind.
Cognify queue & latency
iHow fast Layer-2 extraction keeps up with writes, one point per minute: enqueued (memories written) vs cognified (facts + KG extracted), with the pending backlog on the right axis. A rising backlog or latency means the local extractor is falling behind.