RD Cortex Console

Shared-memory control plane

Sign in with SSO
or
Use email & API token
RD Cortex
console
Read-only access — you can view every page and control, but only an admin can make changes. Ask an admin to grant you access.
Corpus

Memory at a glance

Loading metrics…
Namespaces

What each space holds

NamespaceSummaryMemories
Loading…
What Cortex holds, over time i
Memories — Layer 1
Loading…
Facts — Layer 2
Loading…
By tool

Calls, health & latency per tool

ToolCalls Errorsp50p95
Loading…
By type

Memories per type

TypeMemoriesShare
Loading…
By user

Most active users

UserSearchAdd DeleteOther TotalLast seen
Loading…
Architecture

How Cortex works

Data model

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.

The three axes

Namespace × Type × Topics

Namespace access

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.
user:vladimir@…org:rdteam:rd-eng
Type shape

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.
factstatusprocedurecodedeployment
Topics subject

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.
github-prspeopledeployments
Anatomy

Fields of a memory

FieldAxis / roleNotes
namespaceaccessWhere it lives; the security boundary. Required.
typeshapeOne of the closed vocabulary (in metadata.type). Required.
topics[]subjectOpen, multi-valued organisation axis.
contentpayloadThe verbatim text (Layer 1).
author · sourceprovenanceWho wrote it (bound to the caller) and where it came from.
created_at · valid_atbitemporalWhen recorded vs. when true — drives currency & supersession.
superseded_bylifecycleSet when a newer memory replaces this one (soft-delete).
content_hashdedupExact-duplicate signal on write.
embeddingvector768-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.

Recall

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.

i i show i
Searches run here are not counted in Analytics (usage & search-quality metrics) — this is a diagnostic tool, so it doesn't inflate team-usage numbers. It also runs under your console login, not an MCP token.
Results appear here. This is read-only — it never changes stored memory.
Access control

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.

Add user
UserRoleMember of
Loading…
Access control

Teams

A team is a shared team: namespace; membership is a grant pointing at it.

Add team team: first member
Loading…
Access control

Access matrix

Rows are principals, columns are shared namespaces. Click a cell to grant or revoke — the change applies immediately.

Loading…
granted no access
Access control

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.

Mode

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.

Admin console

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.

Connect instructions

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).

Memory types

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.

+ Add type
Guardrails

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.

PriorityScopeText
Loading…
Collectors

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.

Run daily at
Collectors

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.

Loading…
Integrations

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.

Loading…
Adoption

How the team uses Cortex

Loading usage…
Indexing & embedding

Write path

how memories become searchable
Loading…
Retrieval & ranking

Read path

how a search is answered
Loading…
Recall quality

Search quality vs. corpus size

i

Label-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.

Loading…
Knowledge graph

KG growth over time

i

Cumulative 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.

Loading…
Layer 2 · cognify

Cognify queue & latency

i

How 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.

Loading…
Loading…
By tool

Tool activity over time

Calls per day, by tool
Loading…
Mean latency per day (ms), by tool
Loading…
By user

User activity over time

Search calls per day, by user
Loading…
Add calls per day, by user
Loading…
By source

Memories collected over time

Memories written per day, by collector source — each collector self-registers in the import_runs ledger
Loading…