Applications and Sources
Register applications with a home per person and mounted sources, sync sources into containers that follow each source's own sharing, and see how agent writes carry what they read.
A tenant's memory comes from three places: its sources (Slack, Teams, Drive, Confluence, Notion, Discord), its applications (a support desk, a sales assistant) and the people and agents that work in them. 0.14.0 gives each of them a place in the access model.
Applications
An application record registers one application in one tenant: the memory kinds it writes, a home
for each person, the sources it mounts, and its limits. PUT /v1/apps/{id}?scope=<tenant root>
(an Owner of the tenant root, or the operator key) writes it:
curl -sS -X PUT "$CORTEX_URL/v1/apps/support?scope=org:acme" \
-H "Authorization: Bearer $CORTEX_API_KEY" -H "Content-Type: application/json" \
-d '{"kinds": ["note", "decision"], "limits": {"requests_per_minute": 600}}'{"id": "support", "path": "org:acme/app:support",
"members": [{"actor": "service:support", "role": "owner"}],
"home": "org:acme/app:support/user:{person}",
"kinds": ["note", "decision"], "mounts": [], "limits": {"requests_per_minute": 600},
"consistency_token": "ct1.eyJy…"}-
Homes. Each person's memory in the application lives at
org:acme/app:support/user:<person>. A person's first write there registers it as their own private region, admitting the application:curl -sS -X POST "$CORTEX_URL/v1/experience?wait=indexed" \ -H "Authorization: Bearer $ALICE_TOKEN" -H "X-Cortex-Actor: user:alice" \ -H "Content-Type: application/json" \ -d '{"scope": "org:acme/app:support/user:alice", "modality": "conversation", "content": {"kind": "message", "role": "user", "text": "Customer 4411 asked for a refund on the annual plan."}}' -
Mounts.
mountslists scopes ({"scope": …}) or prefixes of source containers ({"prefix": …}) the application reads beside the person's home. A read withmounts: "default"adds them to its read set. Every mount must be outside private regions and already admitted for the application. -
Admission. In a tenant whose root companion sets
apps_enforced, a token that names an application (app: "service:support") reads and writes only where that application is admitted (403 APP_NOT_ADMITTEDelsewhere). Admissions are written with change sets. -
Names live in the directory:
display_nameon the record is refused. Name the application withPUT /v1/directory/org:acme%2Fapp:support?scope=org:acme{"display_name": "Support desk"}. -
Limits.
requests_per_minutelimits the application's chains in the tenant (429past it). A tenant holds at most 8 application records by default (CORTEX_LIMIT_APPS). -
Reference material. An application's reference root,
app:<key>registered with"reference": trueby the operator key, holds documents every chain of that application reads, from any tenant: product docs shared by every customer's assistant, for example. Only the application's own principal writes there.
Sources and containers
A connector in container mode writes each permission unit of its source (a channel, a folder, a
space) into its own container, <tenant>/source:<system>/container:<key>, and each direct or group
conversation into a private group, so the memory follows the source's own sharing:
- each container's roster is built from the source's members, resolved to directory principals
(
POST /v1/access/accounts/resolve); an account the directory does not know grants nothing; - when an item's sharing changes,
POST /v1/access/rehomemoves its events to the right container without erasing them; - after each sync, the connector records how far it got (
PUT /v1/access/watermarks), and a read withcoverage: truereports a source whose last sync is older thanCORTEX_SOURCE_WATERMARK_MAX_AGE_S(a day by default) as a gap.
cortexdb-connectors 0.3.0 adds container mode for Slack, Teams, Discord, Google Drive, Confluence and
Notion: set CORTEXDB_CONTAINER_ROOT to an org-tree tenant's root. Without it every connector writes
exactly as before. cortexdb-sync migrate-containers <connector> [--dry-run] moves a tenant's existing
items onto containers. See Connectors.
Agent writes and redirects
An agent's write is only as shareable as what it read. Since 0.14.0 the server keeps a read record per
agent chain and run, and a write by an agent chain token
carries the homes of what its run was served. The written memory is then served only to readers of all
of those homes, and erasing any memory it was made from erases it too. A caller can also declare
provenance (up to 1,024 event ids) on a write.
When a write cannot carry that safely (the run read a private region, carries a taint, read from more
than 16 homes, or its read record is unknown, as after a restart), it is redirected to the writer's
own private space, and the answer says where it landed:
# An agent working for Alice, in run run-42, after reading her private region,
# writes to a group she is a Writer of:
curl -sS -X POST "$CORTEX_URL/v1/experience" \
-H "Authorization: Bearer $AGENT_TOKEN" -H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{"scope": "org:acme/group:payments", "modality": "conversation",
"content": {"kind": "message", "role": "assistant", "text": "Draft: migration checklist for Postgres 17."},
"idempotency_key": "agent-draft-0001"}'
# 202 {"event_id":"evt_…","status":"captured",…,"redirected_to":"org:acme/user:alice"}The draft stays with Alice. To share it, Alice herself publishes it: agents draft, people publish.
See also
Operator Modes and Break-Glass
What the static operator key may read under allow, audit and deny, the presets' defaults, the break-glass header, a tenant's own mode, and the access report that shows when it is safe to tighten.
Erasure and Legal Holds
Erase a person, an outside party or a whole tenant with one job and one receipt, keep data under a legal hold, and forget by the keys a client already holds.