How ai‑memory's Per‑Operator Memory Slots Isolate Context Injection Between Shared‑Server Users

ai‑memory isolates per‑operator context through deterministic namespace derivation, read‑time SlotVisibility filtering, and write‑time SlotPlacement guards that prevent cross‑user slot injection.

ai‑memory provides a memory layer for AI‑augmented workflows, designed to run on shared servers where multiple operators might coexist. Its per‑operator memory slots feature ensures that one user cannot inject context into another user's session brief—a critical security boundary for multi‑tenant deployments. This article explains the three‑layer isolation mechanism implemented in the akitaonrails/ai-memory repository.

Namespace Derivation with IdentityKey

Every operator in ai‑memory is represented by an IdentityKey type. When converted to a storage path, it produces a deterministic, URL‑safe identifier through IdentityKey::path_segment().

In crates/ai-memory-core/src/slots.rs lines 15–21, the namespace is derived as follows:

  • IdentityKey::User("alice")path_segment()"u-alice"
  • Personal slots live at "_slots/{namespace}/{filename}"

This design eliminates raw username injection. Because the segment is produced by a typed transformation rather than string concatenation, glob characters or path traversal sequences in usernames cannot create wildcard namespaces that match other operators' slots.

SlotVisibility: Read‑Time Filtering

The SlotVisibility enum controls which _slots/* pages a viewer may see. It is constructed via SlotVisibility::for_viewer(per_user, viewer) and evaluated through SlotVisibility::allows.

From crates/ai-memory-core/src/slots.rs lines 67–85:

  • When [slots] per_user = true in configuration, the viewer's namespace is extracted from their IdentityKey
  • Shared slots (paths without an owner namespace, such as "_slots/current-focus.md") are always allowed because slot_owner() returns None
  • Personal slots are allowed only if slot_owner(path) == viewer_namespace

The session_brief_pages_with_slot_visibility function in crates/ai-memory-store/src/reader.rs lines 4392–4406 applies this filter to all briefing queries. This ensures that even if a malicious actor somehow guesses another user's slot path, the query layer drops it before the brief is assembled.

SlotPlacement: Write‑Time Protection

To prevent context injection—where one operator writes text that appears in another's brief—ai‑memory enforces namespace ownership on every upsert. The slot_placement function in crates/ai-memory-core/src/slots.rs lines 64–81 examines:

  • The target slot path
  • The writer's IdentityKey (if provided)

It returns one of three outcomes:

Outcome Meaning
OwnNamespace Writer owns this slot; write proceeds
RewriteToOwn Redirect into writer's personal namespace
ForeignNamespace Reject: writer cannot inject into another's slot

This guard operates before any page content reaches storage. A shared‑server deployment can safely expose write endpoints because the engine itself prevents cross‑boundary injection.

Practical Isolation Examples

Creating a Personal Slot

use ai_memory_core::{IdentityKey, NewPage, PagePath, Tier};
use ai_memory_store::Store;

let store = Store::open("/tmp/ai-memory").unwrap();
let ws = store.writer.get_or_create_workspace("default".into()).await.unwrap();
let proj = store.writer.get_or_create_project(ws, "demo".into(), None).await.unwrap();

let alice = IdentityKey::User("alice".into());
let alice_slot = format!("_slots/{}/current-focus.md", alice.path_segment());

store.writer
    .upsert_page(NewPage {
        workspace_id: ws,
        project_id: proj,
        path: PagePath::new(&alice_slot).unwrap(),
        title: "Alice’s focus".into(),
        body: "Working on feature X".into(),
        tier: Tier::Semantic,
        frontmatter_json: serde_json::json!({}),
        pinned: true,
        links: vec![],
        author_id: None,
        expires_at: None,
        entities: vec![],
    })
    .await
    .unwrap();

This creates "_slots/u-alice/current-focus.md"—visible only to Alice when per_user isolation is enabled.

Querying with Visibility Constraints

use ai_memory_core::SlotVisibility;

let vis = SlotVisibility::for_viewer(true, Some(&alice));
let pages = store
    .reader
    .session_brief_pages_with_slot_visibility(ws, proj, 100, 100, vis)
    .await
    .unwrap()
    .0;
// Contains Alice's slot, excludes all other personal slots

Blocked Cross‑User Write

let bob = IdentityKey::User("bob".into());
let bob_slot = format!("_slots/{}/current-focus.md", bob.path_segment());

let placement = ai_memory_core::slot_placement(&bob_slot, Some(&alice));
assert_eq!(placement, ai_memory_core::SlotPlacement::ForeignNamespace);
// Write rejected: Alice cannot inject context into Bob's namespace

Integration Across Surfaces

The same visibility rules apply consistently:

Summary

  • Deterministic namespaces from IdentityKey::path_segment() prevent username‑based traversal attacks
  • SlotVisibility filters reads at query time, dropping unauthorized personal slots from session briefs
  • SlotPlacement blocks writes to foreign namespaces, eliminating context injection vectors
  • Consistent application across HTTP, MCP, and internal storage APIs ensures no leakage paths

Frequently Asked Questions

What happens if per_user is disabled in configuration?

When [slots] per_user = false, SlotVisibility::for_viewer returns SlotVisibility::All. All slots—both shared and personal—become visible to every viewer. This preserves backward compatibility for single‑user deployments but removes isolation guarantees.

Can an operator access another user's slot if they guess the exact path?

No. The SlotVisibility::allows check in reader.rs validates ownership against the viewer's derived namespace, not just the string path. Even with exact path knowledge, unauthorized slots are filtered from briefing results.

How does ai‑memory handle legacy shared slots?

Slots at paths like "_slots/current-focus.md" without an owner namespace are treated as shared. slot_owner() returns None for these, and SlotVisibility::allows always permits them regardless of viewer identity.

Where is the isolation mechanism tested?

The slot_visibility.rs test suite in crates/ai-memory-store/tests/ covers namespace collision resistance, cross‑operator visibility blocking, and recent‑page pointer sanitization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →