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:
| Field | Type | Example | Meaning |
|---|---|---|---|
owner | string | "main" | The agent that originally wrote this note. Immutable after creation. |
readers | string[] | ["main"] or ["*"] | Which agents can retrieve this note. ["*"] means all agents. |
writers | string[] | ["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.
// 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
grepor query Qdrant foragent_id = "shared"to audit all shared notes at any time - A bug that fails to set
agent_idfalls 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:
| Dimension | Mode A (default) | Mode B (dedicated collection) |
|---|---|---|
| Qdrant collections | One (amem_notes) | One per agent (amem_notes_dev, etc.) |
| Isolation mechanism | agent_id payload filter at query time | Separate Qdrant collection |
| Shared notes | agent_id="shared" in same collection, visible to all agents | No cross-collection sharing — shared scope not visible in Mode B |
| Setup complexity | None (default) | Requires collection field in agent config |
| Index efficiency | Single index, filter overhead | Separate indexes, no filter overhead |
| Cross-agent sharing | Supported via agent_id="shared" | Not supported across agents |
| Physical data separation | Logical only | Complete (different Qdrant namespaces) |
| Recommended for | Most use cases | High-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
devagent must be fully sandboxed and must never see anymainnotes, 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
{
"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
{
"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
{
"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 defaultamem_notescollection, Mode Adev— uses defaultamem_notescollection withagent_id="dev"filter, Mode A (shares the collection withmain, can see shared notes)sandbox— uses dedicatedamem_notes_sandboxcollection, 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"):
- The plugin constructs a
MemoryNotewith:agent_id: "main"owner: "main"readers: ["main"]writers: ["main"]
- The note is stored in Qdrant under
amem_notes(Mode A) oramem_notes_main(if Mode B configured). - When
devcallsmemory_search("vendor account"), the query filteragent_id IN ["dev", "shared"]excludes this note —devnever 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:
agent_id: "shared",owner: "<writing agent>",readers: ["*"],writers: ["<writing agent>"]- Stored in Qdrant in the shared collection (
amem_notes). - All agents'
searchMemorycalls includeagent_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:
- Ownership ambiguity — Shared notes may have been written by
mainbut are consumed bydev. Lettingdev's consolidation merge or modify them would violate theownerguarantee. - 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.
- writers enforcement (future) — When Story 33 implements
writersfield enforcement, only theowneragent will be able to modify shared notes. Until then, excluding shared notes from consolidation provides equivalent protection.
What consolidation does with each scope
| Note type | Included in consolidation? | Why |
|---|---|---|
Private (agent_id = self) | ✅ Yes | Normal dedup and merge |
Shared (agent_id = "shared") | ❌ No | Excluded to prevent cross-agent modification |
| Other agent's private notes | ❌ No | Filtered out by agent_id query |
Access fields reference
Full combination table
owner | readers | writers | agent_id | Meaning |
|---|---|---|---|---|
"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
| Field | Enforced? | Since | Notes |
|---|---|---|---|
owner | ✅ Schema | Story 32 | Set at write time, not modifiable |
readers | ✅ Query filter | Story 32 | agent_id IN [self, "shared"] filter applied to all searches |
writers | ❌ Schema only | Story 32 | Stored 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:
| Dimension | Purpose | amem equivalent |
|---|---|---|
user_id | Separate memories by human user | Not implemented (single-user local) |
agent_id | Separate memories by AI agent | ✅ agent_id field |
run_id | Separate memories by conversation/session | Not implemented (single-session local) |
app_id | Separate memories by application | Not 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 operatorrun_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_idaccidentally 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:sharedorfilter: { key: "agent_id", match: { value: "shared" } }gives a clean audit list- Missing
agent_idis 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 level | amem mapping | Visibility |
|---|---|---|
| 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 collections | Isolated, 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:
- Cross-agent reads — Agent B reads Agent A's private notes via a shared memory store
- Shared memory poisoning — A malicious or compromised agent writes adversarial content to the shared scope, affecting other agents' retrieval
- Consolidation spillover — An agent's consolidation pass accidentally modifies another agent's notes
openclaw-amem addresses all three:
- Cross-agent reads —
agent_idfilter at query time prevents Agent B from reading Agent A's private notes. In Mode B, separate collections provide physical isolation. - Shared memory poisoning — Currently mitigated by the quality gate (content < 10 chars rejected, ephemeral flagging). Future Story 33
writersenforcement will prevent unauthorized modification of shared notes. - 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)
writersenforcement is planned (progressive hardening)
What is NOT yet protected
writersenforcement — Story 32 stores thewritersfield but does not enforce it. Adevagent could technically evolve or soft-delete a shared note written bymain. 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, orsoftDeletecall will first checkwritersincludes the calling agent - Write attempts by unauthorized agents will return a
PERMISSION_DENIEDerror - The consolidation loop will double-check
writersbefore 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
| Citation | arXiv | Role in amem design |
|---|---|---|
| Chhikara et al., Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory, ECAI 2025 | arXiv:2504.19413 | Four-dimensional scope model; explicit vs implicit shared marker comparison |
| Multi-Agent Memory from a Computer Architecture Perspective, 2026 | arXiv:2603.10062 | Private/shared/distributed memory hierarchy; access protocol design |
| Security of Long-Term Memory in LLM Agents, 2026 | arXiv:2604.16548 | Isolation-by-default principle; memory leakage attack vectors |
| Kerestecioglu et al., Human-Inspired Memory Architecture for LLM Agents, Microsoft, 2026 | arXiv:2605.08538 | Sleep-phase consolidation design (related to amem's 02:30 AM consolidation) |
| Governing Evolving Memory in LLM Agents: SSGM Framework, 2026 | arXiv:2603.11768 | Memory evolution taxonomy (EVOLVE/CONFLICT/EXPAND/NEW) |
| Graph-based Agent Memory: Taxonomy, Techniques, and Applications, 2026 | arXiv:2602.05665 | Conflict detection in graph memory updates |
| Memory in the LLM Era, 2026 | arXiv:2604.01707 | Memory operations taxonomy |
| Xu et al., A-MEM: Agentic Memory for LLM Agents, NeurIPS 2025 | arXiv:2502.12110 | Core architecture that amem implements |
