Skip to content

Memory access

Hadron is a multi-tenant memory platform. Multiple organizations, agents, apps, and end users share the same server, and the platform — not the calling app — decides who can read and write each piece of memory. This page explains how those decisions are made.

If you're new, start with Understanding Memory for what memory is, then Subscriptions for the App↔Agent and User↔Agent concepts, then come back here for how the gates compose.

The four memory classes

Every Memory row carries a class field — one of four values. Each class is governed by a different entity, with different access semantics. Knowing which class a memory is tells you most of what you need to know about who can touch it.

Class Purpose Governed by Strict ownership?
system The Agent's own definition: conversation designs, prompts, partials, topics. The Agent's "brain." Universally read-only from any App context (008-agent-installation R-10). Edits happen in the Agent's ownership surface, not through any App. No
app Records the App writes through the Agent — chat history, extracted facts, generated artifacts. Scoped per install. Memory.app_id — only sessions through that same App can read/write. Per Agent.memoryProvisioning.appMemory: 'shared' keys on (app_id, agent_id); 'user' keys on (app_id, agent_id, user_id). No
knowledge Reference content the Agent reads from. Domain articles, lookup tables, training material. Read-mostly. Per-Agent (not per-install) — same Agent shared across two installs sees the same knowledge memory. AgentMemoryItem.role per-attached-memory (read or read-write). No
personal Per-user records: a single user's chat history, preferences, private notes. Keyed on (app_id, user_id) — privacy-isolated across installs of the same Agent. AgentSubscription(user, app.agent_id) AND Memory.app_id = app.id for the App-keyed write path. Yes — Memory.userId only.

The last column matters. Three of the four classes follow normal access rules — ADMIN/OWNER bypasses, org-level memberships, cross-org subscriptions. The fourth (personal) does not. A user's personal memory is theirs alone. Org admins of the Agent's owning org cannot read it. Org admins of the user's personal org cannot read it. Even platform-level OWNER cannot read it. Only the user themselves.

The four-gate cross-org chain

A real call from an App to a Memory crosses up to four organization boundaries. Each boundary has a gate. All gates must clear, in order. Failure at any gate denies the call with a typed error indicating which layer fired.

                                 ┌──────────────┐
        ┌──────────────┐         │   OrgD       │
        │   OrgA       │         │ (knowledge   │
        │  (App)       │         │  publisher)  │
        │              │         │  ┌────────┐  │
        │  ┌────────┐  │         │  │ Memory │  │
        │  │  App   │  │         │  └────────┘  │
        │  └───┬────┘  │         └──────────────┘
        └──────┼───────┘                ▲
               │  ┌──────────────┐      │
               │  │   OrgB       │      │
               │  │  (Agent)     │      │
               │  │              │      │
               │  │  ┌────────┐  │      │
               └──┼─→│ Agent  │──┼──────┘
                  │  │   │    │  │
                  │  │ system │  │
                  │  │ memory │  │
                  │  └────────┘  │
                  └──────────────┘
          ┌────────────┴────────────────┐
          │  personal-{user}            │
          │  (User's personal org)      │
          │                             │
          │   ┌────────────┐            │
          │   │ Memory:    │            │
          │   │ personal   │            │
          │   │ (User-     │            │
          │   │  owned)    │            │
          │   └────────────┘            │
          └─────────────────────────────┘

The gates, in evaluation order:

Gate 1 — App ↔ Agent

Predicate: App.agent_id IS NOT NULL AND Agent.visibility allows the App's organization. Cross-org installs additionally require an active AgentOrgGrant(App.organization_id, App.agent_id). Same-org installs auto-pass even without the grant row.

Agent.visibility values:

  • PUBLIC — any org can install this Agent (subject to having an active AgentOrgGrant).
  • ORGANIZATION — only the Agent's owning organization can install. (This is the common case for internal chatbots — e.g., Juno is ORGANIZATION-visible inside Micromentor.)
  • PERSONAL — only the Agent's creator. (Agent.visibility is its own AgentVisibility enum; the creator-only value is PERSONAL, not PRIVATE — that word is reserved for the memory private class.)

Failure: denied at the App↔Agent layer.

Gate 2 — Agent ↔ Memory

For knowledge-class memories (the cross-org case): AgentMemoryItem(agent, memory) exists; Memory.visibility allows the Agent's organization; if cross-org, MemorySubscription(orgB, memory) exists and any required MemoryLicense is valid.

For system-class memory: always co-org with the Agent (it's the Agent's own design). Access is implicit; the gate just confirms memory.id = agent.systemMemoryId.

For app-class memory: keyed on (app_id, agent_id) (or (app_id, agent_id, user_id) for per-user-app-memory Agents). The gate checks memory.app_id = app.id.

Failure: denied at the Agent↔Memory layer.

Gate 3 — User ↔ Agent (personal-class only)

Predicate: AgentSubscription(user, app.agent_id) exists AND is active (activated, not revoked, not expired) AND Memory.app_id = app.id.

This gate fires only when the call touches a personal-class memory. For other classes, it's a no-op.

The Memory.app_id = app.id half is what gives multi-install isolation: Mike's personal memory at Juno-on-MicroMentor lives at (microMentorAppId, mike.id); his personal memory at Juno-on-Company-X lives at (companyXAppId, mike.id). A session through one App is denied access to the other's data even though both Apps deploy the same Juno Agent.

Failure: denied at the User↔Agent layer. The Agent has no permission to write to this user's personal memory through this call.

Gate 4 — Effective role

Predicate: the most-restrictive composition of all relevant role values:

  • AgentMemoryItem.role (caps per-attached-memory writes)
  • MemorySubscription.role (caps cross-org writes)

(Pre-008 also included AppAgent.role. That's gone — system memory is universally read-only from any App, so no role-based cap exists at the App↔Agent layer.)

For personal and private memories, the strict-ownership rule from §"The four memory classes" also applies: even an ADMIN/OWNER who would otherwise satisfy the previous gates cannot read a personal/private memory they do not own. (Ownership is keyed on the memory class via Memory.userId; their visibility is null.)

Failure: read may succeed, but write is downgraded to denial.

Concrete example: the Juno chatbot

Juno is a chatbot Agent owned by Micromentor (visibility = ORGANIZATION). End user Alice signs up to Micromentor as an OrgMember and starts a Juno conversation.

Gate Predicate Status
1 App-keyed call from Micromentor's Juno frontend App; App.agent_id = juno.id; Agent.visibility = ORGANIZATION and the App is in Micromentor (same-org install auto-passes)
2 Juno's writes are personal-class for Alice — M.class = personal. Memory at (juno-app.id, alice.id).
3 AgentSubscription(alice, juno) was auto-provisioned on Alice's first message and is active. Memory.app_id = juno-app.id matches the calling App's id.
4 Strict ownership: M.userId = alice and ctx.userId = alice — match. Effective role is read-write through the subscription.

The call succeeds. Now suppose a Micromentor ADMIN (call them Bob) tries to read Alice's Juno memory through the portal:

Gate Predicate Status
1 Bob is JWT-authenticated, not App-keyed. Gate 1 doesn't fire (no App in the call). n/a
Strict ownership M.class = personal, M.userId = alice, ctx.userId = bob.

Bob's read is denied. Even though he's an org ADMIN of Juno's owning organization. Even though he could see every other knowledge memory in Micromentor. personal (and private) memories are strictly owner-only.

Why this matters

The four-gate chain plus strict ownership is not paranoia — it's the trust boundary Hadron promises to chatbot operators and their end users.

  • A chatbot operator's customers can give the bot personal context (medical history, financial details, private notes) knowing that the operator's own employees cannot read it.
  • An employee leaving the operator org (and losing their org membership) doesn't auto-cancel any customer's AgentSubscription; the customer's data stays accessible to them.
  • A bug in the chatbot frontend that "remembers the wrong user" cannot accidentally serve Bob's personal memory to Alice; the personal-memory access path requires the call to come on behalf of userId = bob AND for Memory.userId = bob.
  • Memory ownership flows down to the end user — they hold the keys, not the org that provisioned the chatbot.

These guarantees cost a few extra database lookups per call (well within the existing access-resolution budget). They are non-negotiable.

What if a gate evaluation looks wrong?

Common debugging shapes:

Symptom Likely gate Check
App-keyed call denied; nothing user-related involved Gate 1 Is App.agent_id non-null? Is Agent.visibility compatible with the App's org? For cross-org installs, is the AgentOrgGrant(App.organization_id, App.agent_id) active?
App-keyed call to a knowledge memory denied; cross-org Gate 2 Is there a MemorySubscription from the Agent's org to the memory's org? Is any required MemoryLicense valid?
App-keyed call to a personal memory denied Gate 3 Is AgentSubscription(user, agent) active? Does Memory.app_id match the calling App's id? (A common cause: the user has personal memory in a sibling install of the same Agent — those are isolated.)
Read succeeds but write fails Gate 4 What's the most-restrictive role in the composition? Often AgentMemoryItem.role = read capping a write.
User can read their own personal memory directly but not through the Agent Gate 3 (revoked) Is the AgentSubscription revoked? The Agent is denied; the user is fine.

What's next

  • Agents and Apps — the class/instance relationship that motivates the per-install isolation in gate 3
  • Subscriptions — the AgentOrgGrant (Org↔Agent) and AgentSubscription (User↔Agent) entities in detail
  • Understanding Memory — what each memory class actually contains
  • Architecture — entity hierarchy for the broader picture