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 what the security and architectural rationale is.


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 would pollute dev's retrieval results and vice versa. More critically, a compromised or misbehaving agent could 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 written by an agent is private to that agent unless explicitly published to the shared scope.

Research background

The design is informed by:

  • [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): Models agent memory as an analogy to 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: 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, it is tagged with that agent's agent_id and a readers list containing 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

Shared memory is written 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 relying on 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. It is set at write time and never modified, 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 field is stored in Qdrant but not checked at write time. Enforcement is planned for Story 33.

Example: A shared note written by main has writers: ["main"], signaling intent that only main should modify it — 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 footgun — a bug that forgets to set agent_id accidentally creates a shared (leaky) memory.

amem uses agent_id="shared" as an explicit, deliberate opt-in. The consequence:

  • 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, which is not treated as shared — it is treated as a malformed write and rejected or stored 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 (e.g., 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, shared notes written by main are NOT visible to dev. 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. Shared notes (agent_id="shared") are visible to all agents via automatic filter inclusion.

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. No cross-collection queries are made.

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 note is stored in Qdrant under amem_notes (Mode A) or amem_notes_main (if Mode B 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, shared notes are written via the internal agent_id="shared" scope parameter. A future user-facing API (e.g., 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. Stored 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:

must: [
  { key: "is_active", match: { value: true } },
  { key: "agent_id", match: { any: ["<self>", "shared"] } }
]

This ensures:

  • Soft-deleted notes (is_active: false) are always excluded
  • Only the calling agent's private notes and globally shared notes are returned

Mode B search (own collection only)

In Mode B, the plugin targets the agent's dedicated collection directly. No agent_id filter is needed — the collection itself provides isolation. Shared notes from other agents' collections are not queried.


Consolidation behavior

Per-agent scoping

The daily consolidation pass (02:30 AM in-process scheduler) is scoped 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

Notes with agent_id = "shared" are explicitly excluded from all agents' consolidation passes. This is intentional:

  1. Ownership ambiguity — Shared notes may have been written by main but are consumed by dev. Letting dev's consolidation merge or modify them would violate the owner guarantee.
  2. Stability — Shared notes are intended to be stable reference facts. Automatic merging of shared notes by any agent could 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, excluding shared notes from consolidation 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

writers enforcement is planned for Story 33. When 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. Therefore:

  • 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, to be implemented alongside an HTTP API layer for potential multi-user setups. It will require minimal changes — adding a user_id payload filter to Qdrant queries.

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

mem0's convention: omitting agent_id from a memory entry makes it accessible to all agents (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 therefore 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 or treated as private — never accidentally shared
  • Per [arXiv:2604.16548]: 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, affecting 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 — Currently mitigated by the quality gate (content < 10 chars rejected, ephemeral flagging). Future Story 33 writers enforcement will prevent unauthorized modification of shared notes.
  3. Consolidation spillover — Consolidation is scoped per agent; shared notes are excluded 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)
  • writers enforcement is planned (progressive hardening)

What is NOT yet protected

  • writers enforcement — Story 32 stores the writers field but does not enforce it. A dev agent could 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 could 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
  • Write attempts by unauthorized agents will return a PERMISSION_DENIED error
  • 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 would need updating. This is planned post-Story 33.

user_id dimension

amem is currently single-user. A user_id dimension is planned alongside an HTTP API layer for potential multi-user deployments. When added, 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.