# How ai-memory Handles Auto-Scoping on Shared Servers: Isolation by Design

> Discover how ai-memory ensures isolation on shared servers. Learn how auto-scoping prevents cross-session interference by using unique scope identifiers for each workspace-project-path tuple.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: internals
- Published: 2026-08-31

---

**ai-memory isolates concurrent work sessions on shared servers by deriving a unique scope identifier from the current working directory, ensuring that hand-offs, observations, and wiki pages remain invisible to clients operating outside the same workspace-project-path tuple.**

When multiple developers or AI agents operate on **shared servers**, preventing cross-contamination of session data becomes critical. The **ai-memory** project solves this through an **auto-scoping** system that automatically binds every session to a derived context based on filesystem location. According to the source code in `akitaonrails/ai-memory`, the server constructs a scope tuple from the workspace, project, and path—effectively creating invisible boundaries that keep each user's data compartmentalized without manual configuration.

## How the Auto-Scoping Mechanism Works

### Deriving the Scope Identifier from the Working Directory

The foundation of auto-scoping lies in filesystem introspection. When a client connects, the server reads the current working directory (or respects an explicit `--cwd` flag) and resolves the active project configuration. In [`crates/ai-memory-core/src/active_project.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/active_project.rs), the `ai_memory_core::active_project` module computes a scope tuple consisting of `(workspace_id, project_id, path)`. This tuple serves as the unique identity for the session's data boundary.

The resolution logic searches for an [`.ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/.ai-memory.toml) file in the directory hierarchy to determine project boundaries, falling back to repository root heuristics when necessary. You can observe this resolution programmatically:

```rust
use ai_memory_core::active_project::ActiveProject;

let active = ActiveProject::from_cwd()?;   // reads .ai-memory.toml or defaults
println!("workspace: {}", active.workspace_id);
println!("project: {}", active.project_id);
println!("path: {}", active.path);

```

### Session Ownership and the Scope Tuple

Once derived, the scope tuple becomes the session's owner identifier. As documented in [`docs/users.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/users.md) at line 26, the server records this identifier at session start. Legacy sessions that contain a `NULL` owner field are treated as shared resources and do not benefit from automatic isolation. This distinction ensures that new sessions are private by default while preserving backward compatibility for older data.

### Automatic Hand-Off Inheritance

Hand-offs—structured data exchanges between sessions—automatically inherit the creating session's scope. When the server generates automatic hand-offs at `SessionStart` or `SessionEnd` events, it invokes `ai_memory_core::routing_snippet` to attach the current scope identifier. As noted in [`docs/users.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/users.md) at line 570, this means a hand-off created in `/tmp/project_a` will only appear to clients whose derived scope matches that specific workspace-project-path combination.

The server creates these scoped hand-offs using the internal API:

```rust
use ai_memory_core::handoff::Handoff;

let handoff = Handoff::new_auto(&session_id, "session_start")?;
ai_memory_mcp::admin::store_handoff(&handoff)?;

```

## Isolation Guarantees on Shared Infrastructure

### Path-Based Separation of Concurrent Sessions

The path-derived scope ensures that two agents running simultaneously on the same server under the same user account remain isolated if they operate from different directories. The test suite in [`crates/ai-memory-mcp/tests/autoscope_multiuser.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/tests/autoscope_multiuser.rs) validates this behavior by spawning separate sessions with distinct working directories and asserting that hand-offs created in one scope are inaccessible from another.

This filesystem-level boundary is verified through automated testing:

```rust
#[tokio::test]
async fn autoscope_isolation() {
    let sess_a = start_session_with_cwd("/tmp/project_a").await;
    let sess_b = start_session_with_cwd("/tmp/project_b").await;
    // sess_a creates a hand‑off
    let h = handoff_create(&sess_a, "test").await;
    // sess_b cannot fetch it
    assert!(handoff_fetch(&sess_b, h.id).await.is_err());
}

```

### Legacy Session Handling and Fallback Behavior

If a client fails to provide a session ID or if the working directory cannot be resolved to a valid project configuration, the server falls back to a global scope. This fallback preserves system operability but triggers a warning that data will be shared across all global-scope clients. The global scope acts as a compatibility layer for pre-scoping deployments while encouraging migration to isolated sessions.

## Configuration and Customization

Operators can tune auto-scoping behavior through the `auto_scope` section in [`ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/ai-memory.toml). As documented in [`docs/mcp-install.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/mcp-install.md) between lines 140 and 157, the default `mode = "per_session"` instructs the server to derive unique scopes for every connection. While the configuration allows adjustment of scoping modes, the core algorithm always prefers an explicit scope over the global fallback, ensuring that intentional isolation takes precedence.

## Summary

- ai-memory derives session isolation from the filesystem path, creating a scope tuple of `(workspace_id, project_id, path)`.
- The `ActiveProject::from_cwd` function in [`crates/ai-memory-core/src/active_project.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/active_project.rs) computes this tuple by inspecting [`.ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/.ai-memory.toml) or repository roots.
- Sessions with valid scopes cannot access hand-offs from other scopes, even when sharing the same server and user account.
- Legacy sessions with `NULL` owners default to shared behavior for backward compatibility.
- The system falls back to a global scope only when path resolution fails, warning operators of potential data sharing.

## Frequently Asked Questions

### How does ai-memory prevent data leakage between users on the same server?

ai-memory prevents leakage by binding every session to a unique scope identifier derived from the working directory. Because the `ai_memory_core::routing_snippet` module filters all hand-off and observation queries by this scope tuple, a session running in `/home/user/project-a` cannot retrieve data from `/home/user/project-b`, regardless of operating system user permissions.

### What happens if a client connects without a session ID?

When a client omits a session ID or provides an unresolvable path, the server falls back to a global scope. This mode maintains system functionality but removes isolation guarantees, logging a warning that subsequent data will be visible to all other global-scope clients.

### Can I disable auto-scoping for legacy compatibility?

While you cannot fully disable scope checking, you can configure legacy-like behavior by omitting [`.ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/.ai-memory.toml) configurations and allowing sessions to default to the global scope. Alternatively, adjusting settings in the `auto_scope` section of [`ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/ai-memory.toml) can relax isolation, though the system always prefers explicit scope definitions where detected.

### How is the workspace and project ID determined from the filesystem?

The server traverses the directory hierarchy from the current working directory upward, searching for [`.ai-memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/.ai-memory.toml) files that define project boundaries. If found, the file contents determine the `workspace_id` and `project_id`; otherwise, the system uses the repository root name and directory path as identifiers, as implemented in [`crates/ai-memory-core/src/active_project.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/active_project.rs).