Skip to content

Agent Isolation

openclaw-amem gives every OpenClaw agent its own private memory namespace. This page explains how isolation works, how shared memory is published, what the access fields mean, how to configure Mode A vs Mode B, and the rationale for security and architecture.


Introduction

In a multi-agent OpenClaw setup — for example, main (your daily driver) and dev (a sandboxed coding assistant) — each agent accumulates memories about its own work context. Without isolation, memories from main will pollute dev's retrieval results and vice versa. More critically, a compromised or misbehaving agent can read private information stored by another agent through shared memory poisoning (arXiv:2604.16548).

openclaw-amem addresses this with an isolation-by-default model: every memory that an agent writes is private to that agent unless the agent explicitly publishes it to the shared scope.

Research background

The following papers informed the design:

  • [arXiv:2604.16548]Security of Long-Term Memory in LLM Agents (2026): Memory leakage in multi-agent systems most commonly occurs via shared memory stores. The paper argues isolation should be the default, with sharing as an explicit, auditable exception.
  • [arXiv:2603.10062]Multi-Agent Memory from a Computer Architecture Perspective (2026): The paper models agent memory as an analogy to the computer memory hierarchy — L1 cache (private), RAM (shared), NUMA (distributed). amem's Mode A maps to the L1/RAM split. Mode B maps to NUMA.
  • [arXiv:2504.19413]Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory, ECAI 2025: The paper defines a four-dimensional scope model (user_id, agent_id, run_id, app_id). amem's scope model is a deliberate subset — see Comparison with mem0.

Core concepts

Private memory

Private memory is the default. When an agent writes a note, the plugin tags it with that agent's agent_id and a readers list that contains only that agent. No other agent can retrieve it.

When to use private memory: Always, unless you have a specific reason to share. Private memory is appropriate for:

  • Personal task context ("Currently debugging auth middleware, focus on JWT expiry")
  • Agent-specific preferences and conventions
  • Sensitive or proprietary information
  • Work-in-progress notes not ready to share

Shared memory

You write shared memory with agent_id="shared" and readers=["*"]. All agents that query memory will see shared notes mixed into their results.

When to use shared memory: When information is genuinely useful to all agents:

  • Team-wide conventions ("All API endpoints use snake_case JSON keys")
  • Shared credentials or configuration facts
  • Cross-agent context that needs to be consistent ("Production deploy is frozen until 2026-06-20")

The key design decision: amem uses an explicit agent_id="shared" marker rather than the absence of an agent_id field. This makes shared notes immediately identifiable in the Qdrant database and prevents accidental leakage via missing fields. See Design rationale below.

The three access fields

Every MemoryNote carries three access control fields:

FieldTypeExampleMeaning
ownerstring"main"The agent that originally wrote this note. Immutable after creation.
readersstring[]["main"] or ["*"]Which agents can retrieve this note. ["*"] means all agents.
writersstring[]["main"]Which agents can modify this note. Not yet enforced — Story 33.

owner

The owner field records which agent created the note. The plugin sets it at write time and never modifies it, even if the note is later evolved or updated. It provides an audit trail: you can always query Qdrant to see which agent produced which memories.

Example: A note written by dev always has owner: "dev", even if main later reads it via shared access.

readers

The readers field controls which agents' searchMemory calls will return this note.

ts
// Private note — only "main" can retrieve it
readers: ["main"]

// Shared note — any agent can retrieve it
readers: ["*"]

// Future: fine-grained ACL (not yet implemented)
readers: ["main", "dev"]

In Mode A (shared collection), the searchMemory query adds a Qdrant filter:

agent_id IN ["<self>", "shared"]

This means each agent sees only its own private notes plus notes in the shared scope.

writers

The writers field records which agents are authorized to modify (evolve, update, soft-delete) this note. Currently schema-only: the plugin stores the field in Qdrant but does not check it at write time. Story 33 will add enforcement.

Example: A shared note written by main has writers: ["main"]. This signals that only main is the intended modifier, but dev can currently update it without a hard error. This will change in Story 33.

Why explicit shared marker?

mem0 ([arXiv:2504.19413]) uses an implicit convention: omitting agent_id from a memory entry means it is accessible to all agents. This creates a problem: a bug that forgets to set agent_id accidentally creates a shared (leaky) memory.

amem uses agent_id="shared" as an explicit opt-in. The consequences are:

  • You can grep or query Qdrant for agent_id = "shared" to audit all shared notes at any time
  • A bug that fails to set agent_id falls back to the empty-string or missing-field case. The system does not treat this as shared. It treats it as a malformed write and rejects it or stores it as private.
  • Shared notes are visually distinct in Qdrant's web UI

Mode A vs Mode B

openclaw-amem supports two physical isolation modes:

DimensionMode A (default)Mode B (dedicated collection)
Qdrant collectionsOne (amem_notes)One per agent (amem_notes_dev, etc.)
Isolation mechanismagent_id payload filter at query timeSeparate Qdrant collection
Shared notesagent_id="shared" in same collection, visible to all agentsNo cross-collection sharing — shared scope not visible in Mode B
Setup complexityNone (default)Requires collection field in agent config
Index efficiencySingle index, filter overheadSeparate indexes, no filter overhead
Cross-agent sharingSupported via agent_id="shared"Not supported across agents
Physical data separationLogical onlyComplete (different Qdrant namespaces)
Recommended forMost use casesHigh-security sandboxing, compliance requirements

When to use Mode A

Mode A is the right choice for:

  • Standard multi-agent setups where agents share some context
  • Cases where you want shared notes visible to all agents
  • Simpler configuration and operational overhead

When to use Mode B

Mode B is appropriate when:

  • You need complete physical separation (for example, compliance or audit requirements)
  • The dev agent must be fully sandboxed and must never see any main notes, even shared ones
  • You want separate Qdrant collections for independent backup or restore

Note: In Mode B, dev does not see shared notes written by main. If you need cross-agent sharing, use Mode A.


Configuration

Mode A — default, no extra config required

json
{
  "plugins": {
    "allow": ["openclaw-amem"],
    "entries": {
      "openclaw-amem": {
        "enabled": true,
        "config": {
          "agentId": "main",
          "topK": 5
        }
      }
    },
    "slots": {
      "memory": "openclaw-amem"
    }
  }
}

All agents use the default amem_notes collection. The agentId field determines which agent's notes are private. All agents can see shared notes (agent_id="shared") because the filter includes them automatically.

Mode B — dedicated collection per agent

json
{
  "plugins": {
    "allow": ["openclaw-amem"],
    "entries": {
      "openclaw-amem": {
        "enabled": true,
        "config": {
          "agentId": "main",
          "topK": 5,
          "agents": {
            "dev": {
              "agentId": "dev",
              "collection": "amem_notes_dev"
            }
          }
        }
      }
    },
    "slots": {
      "memory": "openclaw-amem"
    }
  }
}

When the dev agent is active, it reads from and writes to amem_notes_dev exclusively. The main agent continues to use amem_notes. The system makes no cross-collection queries.

Full openclaw.json example with multiple agents

json
{
  "plugins": {
    "allow": ["openclaw-amem"],
    "entries": {
      "openclaw-amem": {
        "enabled": true,
        "config": {
          "agentId": "main",
          "topK": 5,
          "agents": {
            "dev": {
              "agentId": "dev"
            },
            "sandbox": {
              "agentId": "sandbox",
              "collection": "amem_notes_sandbox"
            }
          }
        }
      }
    },
    "slots": {
      "memory": "openclaw-amem"
    }
  }
}

In this example:

  • main — uses default amem_notes collection, Mode A
  • dev — uses default amem_notes collection with agent_id="dev" filter, Mode A (shares the collection with main, can see shared notes)
  • sandbox — uses dedicated amem_notes_sandbox collection, Mode B (completely isolated, no shared notes visible)

How shared memory works step by step

Writing a private note (default)

When main calls memory_add("vendor account"):

  1. The plugin constructs a MemoryNote with:
    • agent_id: "main"
    • owner: "main"
    • readers: ["main"]
    • writers: ["main"]
  2. The plugin stores the note in Qdrant under amem_notes (Mode A) or amem_notes_main (if Mode B is configured).
  3. When dev calls memory_search("vendor account"), the query filter agent_id IN ["dev", "shared"] excludes this note — dev never sees it.

Writing a shared note

Currently, you write shared notes via the internal agent_id="shared" scope parameter. A future user-facing API (for example, memory_add(text, scope="shared")) is planned.

When a shared note is written:

  1. agent_id: "shared", owner: "<writing agent>", readers: ["*"], writers: ["<writing agent>"]
  2. The plugin stores it in Qdrant in the shared collection (amem_notes).
  3. All agents' searchMemory calls include agent_id IN [self, "shared"], so all agents retrieve this note.

How searchMemory filters results (Mode A)

Every searchMemory query in Mode A appends a Qdrant payload filter:

json
{
  "must": [
    { "should": [
      { "key": "agent_id", "match": { "value": "<self>" } },
      { "key": "agent_id", "match": { "value": "shared" } }
    ] }
  ],
  "must_not": [
    { "key": "is_active", "match": { "value": false } }
  ]
}

is_active is under must_not rather than must, which is not the same thing: notes written before the field existed have no is_active at all, and requiring true will hide them. Excluding false keeps them.

This ensures:

  • The query always excludes soft-deleted notes (is_active: false)
  • The query returns only the calling agent's private notes and globally shared notes

Mode B search (own collection only)

In Mode B, the plugin targets the agent's dedicated collection directly. The plugin needs no agent_id filter — the collection itself provides isolation. The plugin does not query shared notes from other agents' collections.


Consolidation behavior

Per-agent scoping

The daily consolidation pass (02:30 AM in-process scheduler) runs per agent. When main's consolidation runs, it queries only agent_id = "main" notes. When dev's consolidation runs (if dev has its own consolidation schedule), it queries only agent_id = "dev" notes.

Shared notes are excluded

All agents' consolidation passes explicitly exclude notes with agent_id = "shared". This is intentional:

  1. Ownership ambiguity — Shared notes can be written by main but consumed by dev. If dev's consolidation merges or modifies them, it will violate the owner guarantee.
  2. Stability — Shared notes serve as stable reference facts. Merging shared notes automatically can corrupt or silently alter information that other agents depend on.
  3. writers enforcement (future) — When Story 33 implements writers field enforcement, only the owner agent will be able to modify shared notes. Until then, this exclusion provides equivalent protection.

What consolidation does with each scope

Note typeIncluded in consolidation?Why
Private (agent_id = self)✅ YesNormal dedup and merge
Shared (agent_id = "shared")❌ NoExcluded to prevent cross-agent modification
Other agent's private notes❌ NoFiltered out by agent_id query

Access fields reference

Full combination table

ownerreaderswritersagent_idMeaning
"main"["main"]["main"]"main"Private note written by main, readable only by main
"dev"["dev"]["dev"]"dev"Private note written by dev, readable only by dev
"main"["*"]["main"]"shared"Shared note written by main, readable by all agents
"dev"["*"]["dev"]"shared"Shared note written by dev, readable by all agents
"main"["main", "dev"]["main"]"main"Fine-grained ACL (future, not yet implemented)

Current enforcement status

FieldEnforced?SinceNotes
owner✅ SchemaStory 32Set at write time, not modifiable
readers✅ Query filterStory 32agent_id IN [self, "shared"] filter applied to all searches
writers❌ Schema onlyStory 32Stored in Qdrant but not checked at write/update/delete time

Story 33 will add writers enforcement. When Story 33 is implemented, any write/evolve/delete operation against a note will first check that the calling agent appears in the note's writers list.


Comparison with mem0 scope model

mem0's four-dimensional scope

mem0 ([arXiv:2504.19413], ECAI 2025) defines memory scope across four orthogonal dimensions:

DimensionPurposeamem equivalent
user_idSeparate memories by human userNot implemented (single-user local)
agent_idSeparate memories by AI agentagent_id field
run_idSeparate memories by conversation/sessionNot implemented (single-session local)
app_idSeparate memories by applicationNot implemented (single-app)

Why amem doesn't need run_id and app_id

amem is a single-user local system. There is no multi-tenant SaaS context where multiple users or applications share one memory store. As a result:

  • user_id — unnecessary. amem assumes a single human operator.
  • run_id — unnecessary. amem treats the OpenClaw session as a continuous context, not isolated conversation runs. Consolidation already handles cross-session memory management.
  • app_id — unnecessary. amem is a dedicated plugin for OpenClaw only.

user_id is in the long-term roadmap. The team plans to implement it alongside an HTTP API layer for potential multi-user setups. The only required change is a user_id payload filter added to Qdrant queries.

Why explicit shared marker over mem0's implicit null-scoping

mem0's convention: when agent_id is omitted from a memory entry, all agents can access that entry (implicit shared).

amem's convention: agent_id="shared" is an explicit opt-in (explicit shared).

The problem with implicit null-scoping:

  • A bug that forgets to set agent_id accidentally creates a shared (and thus leaky) memory
  • You cannot distinguish "shared by design" from "shared by accident" in the database
  • Auditing shared notes requires querying for null/missing agent_id, which is awkward in Qdrant

amem's explicit marker:

  • grep agent_id:shared or filter: { key: "agent_id", match: { value: "shared" } } gives a clean audit list
  • Missing agent_id is a write error. The system treats it as private, never accidentally shared.
  • As [arXiv:2604.16548] states: isolation-by-default, sharing is an explicit, auditable exception

Computer architecture analogy

[arXiv:2603.10062] frames multi-agent memory as a computer memory hierarchy:

Architecture levelamem mappingVisibility
L1 cache (per-core private)Private notes (agent_id = self)Only the owning agent
RAM (shared)Shared notes (agent_id = "shared")All agents
NUMA (distributed)Mode B dedicated collectionsIsolated, no cross-agent access

Mode A implements the L1/RAM split: each agent has a private working set with a global shared region. Mode B implements NUMA-style isolation: each agent has its own address space with no shared region.


Security considerations

Memory leakage in multi-agent systems

[arXiv:2604.16548] identifies three primary memory leakage vectors in multi-agent LLM systems:

  1. Cross-agent reads — Agent B reads Agent A's private notes via a shared memory store
  2. Shared memory poisoning — A malicious or compromised agent writes adversarial content to the shared scope and affects other agents' retrieval
  3. Consolidation spillover — An agent's consolidation pass accidentally modifies another agent's notes

openclaw-amem addresses all three:

  1. Cross-agent readsagent_id filter at query time prevents Agent B from reading Agent A's private notes. In Mode B, separate collections provide physical isolation.
  2. Shared memory poisoning — The quality gate currently mitigates this (content < 10 chars rejected, ephemeral flagging). Story 33 writers enforcement will prevent unauthorized modification of shared notes.
  3. Consolidation spillover — Consolidation runs per agent. The system excludes shared notes entirely.

Isolation-by-default principle

The core security principle, per [arXiv:2604.16548]: isolation should be the default, sharing is an explicit exception.

This principle shapes every design decision in openclaw-amem's isolation model:

  • Private is the default note scope (no extra config needed)
  • Shared requires explicit agent_id="shared" (opt-in)
  • Consolidation excludes shared notes (conservative default)
  • Story 33 will add writers enforcement (progressive hardening)

What is NOT yet protected

  • writers enforcement — Story 32 stores the writers field but does not enforce it. A dev agent can technically evolve or soft-delete a shared note written by main. Story 33 will add a hard check.
  • Prompt injection via shared notes — A compromised agent can write adversarial content into a shared note that manipulates another agent's behavior when retrieved. This is a known attack class ([arXiv:2604.16548] §4). Mitigation requires content validation at write time (future work).

Limitations and future work

Story 33: writers enforcement

The writers field is currently schema-only. When Story 33 is implemented:

  • Any updateMemory, evolveNote, or softDelete call will first check writers includes the calling agent
  • Unauthorized agents will receive a PERMISSION_DENIED error on write attempts
  • The consolidation loop will double-check writers before merging shared notes

No cross-collection sharing in Mode B

In Mode B, each agent operates on its own dedicated Qdrant collection. There is no cross-collection query mechanism. If main writes a shared note in amem_notes and dev is in Mode B (amem_notes_dev), dev does not see main's shared note.

This is a deliberate trade-off: Mode B prioritizes physical isolation over sharing convenience. If you need cross-agent sharing, use Mode A.

No fine-grained ACL beyond binary private/shared

Current readers options are:

  • ["<agentId>"] — private (single agent)
  • ["*"] — shared (all agents)

A future fine-grained ACL (readers: ["main", "dev"] for selective sharing between specific agents) is possible without schema changes — only query filter logic will need updating. Implementation is planned for post-Story 33.

user_id dimension

amem is currently single-user. The team plans a user_id dimension alongside an HTTP API layer for potential multi-user deployments. When the team adds it, it will be an additional Qdrant payload filter with no breaking changes to the existing agent_id isolation model.


References

CitationarXivRole in amem design
Chhikara et al., Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory, ECAI 2025arXiv:2504.19413Four-dimensional scope model; explicit vs implicit shared marker comparison
Multi-Agent Memory from a Computer Architecture Perspective, 2026arXiv:2603.10062Private/shared/distributed memory hierarchy; access protocol design
Security of Long-Term Memory in LLM Agents, 2026arXiv:2604.16548Isolation-by-default principle; memory leakage attack vectors
Kerestecioglu et al., Human-Inspired Memory Architecture for LLM Agents, Microsoft, 2026arXiv:2605.08538Sleep-phase consolidation design (related to amem's 02:30 AM consolidation)
Governing Evolving Memory in LLM Agents: SSGM Framework, 2026arXiv:2603.11768Memory evolution taxonomy (EVOLVE/CONFLICT/EXPAND/NEW)
Graph-based Agent Memory: Taxonomy, Techniques, and Applications, 2026arXiv:2602.05665Conflict detection in graph memory updates
Memory in the LLM Era, 2026arXiv:2604.01707Memory operations taxonomy
Xu et al., A-MEM: Agentic Memory for LLM Agents, NeurIPS 2025arXiv:2502.12110Core architecture that amem implements

Released under the MIT License.